summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Astals Cid <[email protected]>2018-02-01 19:42:37 +0100
committerAlbert Astals Cid <[email protected]>2018-02-01 22:53:28 +0100
commit2d8b2c7e95927db1633ecb871ed4100c3e7e3833 (patch)
tree9035904ec4d50fe518b003595bcdf1382057ec63
parentc762b86b2b846212a15279a6d0b8617de1a45bbd (diff)
Add support for cancellable image rendering and text extraction
Summary: Only supported by the pdf backend if using poppler >= 0.63 Sadly had to change the generator API Text cancellation is a bit wobbly still since poppler has large parts of the code where doesn't check for the cancellation flag, but that is something that will get automagically fixed for us if the poppler side is improved Test Plan: Needs https://bugs.freedesktop.org/show_bug.cgi?id=104263 Reviewers: ervin, rkflx Reviewed By: ervin, rkflx Subscribers: #okular Tags: #okular Differential Revision: https://phabricator.kde.org/D9328
-rw-r--r--core/area.h12
-rw-r--r--core/document.cpp230
-rw-r--r--core/document_p.h1
-rw-r--r--core/generator.cpp133
-rw-r--r--core/generator.h65
-rw-r--r--core/generator_p.cpp62
-rw-r--r--core/generator_p.h24
-rw-r--r--core/page.cpp32
-rw-r--r--core/page_p.h4
-rw-r--r--core/rotationjob.cpp11
-rw-r--r--core/rotationjob_p.h3
-rw-r--r--core/textdocumentgenerator.cpp4
-rw-r--r--core/textdocumentgenerator.h2
-rw-r--r--core/tilesmanager.cpp84
-rw-r--r--core/tilesmanager_p.h2
-rw-r--r--generators/chm/generator_chm.cpp3
-rw-r--r--generators/chm/generator_chm.h2
-rw-r--r--generators/djvu/generator_djvu.cpp3
-rw-r--r--generators/djvu/generator_djvu.h2
-rw-r--r--generators/dvi/generator_dvi.cpp4
-rw-r--r--generators/dvi/generator_dvi.h2
-rw-r--r--generators/poppler/CMakeLists.txt11
-rw-r--r--generators/poppler/config-okular-poppler.h.cmake3
-rw-r--r--generators/poppler/generator_pdf.cpp115
-rw-r--r--generators/poppler/generator_pdf.h2
-rw-r--r--generators/xps/generator_xps.cpp4
-rw-r--r--generators/xps/generator_xps.h2
27 files changed, 658 insertions, 164 deletions
diff --git a/core/area.h b/core/area.h
index 6b4cc55..4299b00 100644
--- a/core/area.h
+++ b/core/area.h
@@ -300,6 +300,18 @@ class OKULARCORE_EXPORT NormalizedRect
return pow( distX * xScale, 2 ) + pow( distY * yScale, 2 );
}
+ /// @since 1.4
+ double width() const
+ {
+ return right - left;
+ }
+
+ /// @since 1.4
+ double height() const
+ {
+ return bottom - top;
+ }
+
/**
* The normalized left coordinate.
*/
diff --git a/core/document.cpp b/core/document.cpp
index 00744c5..8db23b3 100644
--- a/core/document.cpp
+++ b/core/document.cpp
@@ -1307,7 +1307,7 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
if ( pixmap )
{
tilesManager = new TilesManager( r->pageNumber(), pixmap->width(), pixmap->height(), r->page()->rotation() );
- tilesManager->setPixmap( pixmap, NormalizedRect( 0, 0, 1, 1 ) );
+ tilesManager->setPixmap( pixmap, NormalizedRect( 0, 0, 1, 1 ), true /*isPartialPixmap*/ );
tilesManager->setSize( r->width(), r->height() );
}
else
@@ -1416,7 +1416,11 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
request->setNormalizedRect( TilesManager::fromRotatedRect(
request->normalizedRect(), m_rotation ) );
- request->setPartialUpdatesWanted( request->asynchronous() && !request->page()->hasPixmap( request->observer() ) );
+ // If set elsewhere we already know we want it to be partial
+ if ( !request->partialUpdatesWanted() )
+ {
+ 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
@@ -1512,18 +1516,29 @@ void DocumentPrivate::refreshPixmaps( int pageNumber )
if ( !page )
return;
- QLinkedList< Okular::PixmapRequest * > requestedPixmaps;
QMap< DocumentObserver*, PagePrivate::PixmapObject >::ConstIterator it = page->d->m_pixmaps.constBegin(), itEnd = page->d->m_pixmaps.constEnd();
+ QVector< Okular::PixmapRequest * > pixmapsToRequest;
for ( ; it != itEnd; ++it )
{
- QSize size = (*it).m_pixmap->size();
+ const QSize size = (*it).m_pixmap->size();
PixmapRequest * p = new PixmapRequest( it.key(), pageNumber, size.width() / qApp->devicePixelRatio(), size.height() / qApp->devicePixelRatio(), 1, PixmapRequest::Asynchronous );
p->d->mForce = true;
- requestedPixmaps.push_back( p );
+ pixmapsToRequest << p;
+ }
+
+ // Need to do this ↑↓ in two steps since requestPixmaps can end up calling cancelRenderingBecauseOf
+ // which changes m_pixmaps and thus breaks the loop above
+ for ( PixmapRequest *pr : qAsConst( pixmapsToRequest ) )
+ {
+ QLinkedList< Okular::PixmapRequest * > requestedPixmaps;
+ pixmapsToRequest.push_back( pr );
+ m_parent->requestPixmaps( requestedPixmaps, Okular::Document::NoOption );
}
foreach (DocumentObserver *observer, m_observers)
{
+ QLinkedList< Okular::PixmapRequest * > requestedPixmaps;
+
TilesManager *tilesManager = page->d->tilesManager( observer );
if ( tilesManager )
{
@@ -1557,10 +1572,10 @@ void DocumentPrivate::refreshPixmaps( int pageNumber )
delete p;
}
}
- }
- if ( !requestedPixmaps.isEmpty() )
m_parent->requestPixmaps( requestedPixmaps, Okular::Document::NoOption );
+ }
+
}
void DocumentPrivate::_o_configChanged()
@@ -2547,6 +2562,16 @@ void Document::closeDocument()
{
d->m_pixmapRequestsMutex.lock();
startEventLoop = !d->m_executingPixmapRequests.isEmpty();
+
+ if ( d->m_generator->hasFeature( Generator::SupportsCancelling ) )
+ {
+ for ( PixmapRequest *executingRequest : qAsConst( d->m_executingPixmapRequests ) )
+ executingRequest->d->mShouldAbortRender = 1;
+
+ if ( d->m_generator->d_ptr->mTextPageGenerationThread )
+ d->m_generator->d_ptr->mTextPageGenerationThread->abortExtraction();
+ }
+
d->m_pixmapRequestsMutex.unlock();
if ( startEventLoop )
{
@@ -3095,6 +3120,82 @@ QString Document::pageSizeString(int page) const
return QString();
}
+static bool shouldCancelRenderingBecauseOf( const PixmapRequest & executingRequest, const PixmapRequest & otherRequest )
+{
+ // New request has higher priority -> cancel
+ if ( executingRequest.priority() > otherRequest.priority() )
+ return true;
+
+ // New request has lower priority -> don't cancel
+ if ( executingRequest.priority() < otherRequest.priority() )
+ return false;
+
+ // New request has same priority and is from a different observer -> don't cancel
+ // AFAIK this never happens since all observers have different priorities
+ if ( executingRequest.observer() != otherRequest.observer() )
+ return false;
+
+ // Same priority and observer, different page number -> don't cancel
+ // may still end up cancelled later in the parent caller if none of the requests
+ // is of the executingRequest page and RemoveAllPrevious is specified
+ if ( executingRequest.pageNumber() != otherRequest.pageNumber() )
+ return false;
+
+ // Same priority, observer, page, different size -> cancel
+ if ( executingRequest.width() != otherRequest.width() )
+ return true;
+
+ // Same priority, observer, page, different size -> cancel
+ if ( executingRequest.height() != otherRequest.height() )
+ return true;
+
+ // Same priority, observer, page, different tiling -> cancel
+ if ( executingRequest.isTile() != otherRequest.isTile() )
+ return true;
+
+ // Same priority, observer, page, different tiling -> cancel
+ if ( executingRequest.isTile() )
+ {
+ const NormalizedRect bothRequestsRect = executingRequest.normalizedRect() | otherRequest.normalizedRect();
+ if ( !( bothRequestsRect == executingRequest.normalizedRect() ) )
+ return true;
+ }
+
+ return false;
+}
+
+bool DocumentPrivate::cancelRenderingBecauseOf( PixmapRequest *executingRequest, PixmapRequest *newRequest )
+{
+ // No point in aborting the rendering already finished, let it go through
+ if ( !executingRequest->d->mResultImage.isNull() )
+ return false;
+
+ if ( newRequest && executingRequest->partialUpdatesWanted() ) {
+ newRequest->setPartialUpdatesWanted( true );
+ }
+
+ TilesManager *tm = executingRequest->d->tilesManager();
+ if ( tm )
+ {
+ tm->setPixmap( nullptr, executingRequest->normalizedRect(), true /*isPartialPixmap*/ );
+ tm->setRequest( NormalizedRect(), 0, 0 );
+ }
+ PagePrivate::PixmapObject object = executingRequest->page()->d->m_pixmaps.take( executingRequest->observer() );
+ delete object.m_pixmap;
+
+ if ( executingRequest->d->mShouldAbortRender != 0)
+ return false;
+
+ executingRequest->d->mShouldAbortRender = 1;
+
+ if ( m_generator->d_ptr->mTextPageGenerationThread && m_generator->d_ptr->mTextPageGenerationThread->page() == executingRequest->page() )
+ {
+ m_generator->d_ptr->mTextPageGenerationThread->abortExtraction();
+ }
+
+ return true;
+}
+
void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests )
{
requestPixmaps( requests, RemoveAllPrevious );
@@ -3115,14 +3216,18 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests,
return;
}
+ QSet< DocumentObserver * > observersPixmapCleared;
+
// 1. [CLEAN STACK] remove previous requests of requesterID
- // FIXME This assumes all requests come from the same observer, that is true atm but not enforced anywhere
DocumentObserver *requesterObserver = requests.first()->observer();
QSet< int > requestedPages;
{
QLinkedList< PixmapRequest * >::const_iterator rIt = requests.constBegin(), rEnd = requests.constEnd();
for ( ; rIt != rEnd; ++rIt )
+ {
+ Q_ASSERT( (*rIt)->observer() == requesterObserver );
requestedPages.insert( (*rIt)->pageNumber() );
+ }
}
const bool removeAllPrevious = reqOptions & RemoveAllPrevious;
d->m_pixmapRequestsMutex.lock();
@@ -3140,12 +3245,10 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests,
++sIt;
}
- // 2. [ADD TO STACK] add requests to stack
- QLinkedList< PixmapRequest * >::const_iterator rIt = requests.constBegin(), rEnd = requests.constEnd();
- for ( ; rIt != rEnd; ++rIt )
+ // 1.B [PREPROCESS REQUESTS] tweak some values of the requests
+ for ( PixmapRequest *request : requests )
{
// set the 'page field' (see PixmapRequest) and check if it is valid
- PixmapRequest * request = *rIt;
qCDebug(OkularCoreDebug).nospace() << "request observer=" << request->observer() << " " <<request->width() << "x" << request->height() << "@" << request->pageNumber();
if ( d->m_pagesVector.value( request->pageNumber() ) == 0 )
{
@@ -3182,7 +3285,44 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests,
if ( !request->asynchronous() )
request->d->mPriority = 0;
+ }
+
+ // 1.C [CANCEL REQUESTS] cancel those requests that are running and should be cancelled because of the new requests coming in
+ if ( d->m_generator->hasFeature( Generator::SupportsCancelling ) )
+ {
+ for ( PixmapRequest *executingRequest : qAsConst( d->m_executingPixmapRequests ) )
+ {
+ bool newRequestsContainExecutingRequestPage = false;
+ bool requestCancelled = false;
+ for ( PixmapRequest *newRequest : requests )
+ {
+ if ( newRequest->pageNumber() == executingRequest->pageNumber() && requesterObserver == executingRequest->observer())
+ {
+ newRequestsContainExecutingRequestPage = true;
+ }
+
+ if ( shouldCancelRenderingBecauseOf( *executingRequest, *newRequest ) )
+ {
+ requestCancelled = d->cancelRenderingBecauseOf( executingRequest, newRequest );
+ }
+ }
+
+ // If we were told to remove all the previous requests and the executing request page is not part of the new requests, cancel it
+ if ( !requestCancelled && removeAllPrevious && requesterObserver == executingRequest->observer() && !newRequestsContainExecutingRequestPage )
+ {
+ requestCancelled = d->cancelRenderingBecauseOf( executingRequest, nullptr );
+ }
+
+ if ( requestCancelled )
+ {
+ observersPixmapCleared << executingRequest->observer();
+ }
+ }
+ }
+ // 2. [ADD TO STACK] add requests to stack
+ for ( PixmapRequest *request : requests )
+ {
// add request to the 'stack' at the right place
if ( !request->priority() )
// add priority zero requests to the top of the stack
@@ -3205,6 +3345,9 @@ void Document::requestPixmaps( const QLinkedList< PixmapRequest * > & requests,
// all handling of requests put into sendGeneratorPixmapRequest
// if ( generator->canRequestPixmap() )
d->sendGeneratorPixmapRequest();
+
+ for ( DocumentObserver *o : qAsConst( observersPixmapCleared ) )
+ o->notifyContentsCleared( Okular::DocumentObserver::Pixmap );
}
void Document::requestTextPage( uint page )
@@ -4859,41 +5002,44 @@ void DocumentPrivate::requestDone( PixmapRequest * req )
qCDebug(OkularCoreDebug) << "requestDone with generator not in READY state.";
#endif
- // [MEM] 1.1 find and remove a previous entry for the same page and id
- QLinkedList< AllocatedPixmap * >::iterator aIt = m_allocatedPixmaps.begin();
- QLinkedList< AllocatedPixmap * >::iterator aEnd = m_allocatedPixmaps.end();
- for ( ; aIt != aEnd; ++aIt )
- if ( (*aIt)->page == req->pageNumber() && (*aIt)->observer == req->observer() )
- {
- AllocatedPixmap * p = *aIt;
- m_allocatedPixmaps.erase( aIt );
- m_allocatedPixmapsTotalMemory -= p->memory;
- delete p;
- break;
- }
-
- DocumentObserver *observer = req->observer();
- if ( m_observers.contains(observer) )
+ if ( !req->shouldAbortRender() )
{
- // [MEM] 1.2 append memory allocation descriptor to the FIFO
- qulonglong memoryBytes = 0;
- const TilesManager *tm = req->d->tilesManager();
- if ( tm )
- memoryBytes = tm->totalMemory();
- else
- memoryBytes = 4 * req->width() * req->height();
+ // [MEM] 1.1 find and remove a previous entry for the same page and id
+ QLinkedList< AllocatedPixmap * >::iterator aIt = m_allocatedPixmaps.begin();
+ QLinkedList< AllocatedPixmap * >::iterator aEnd = m_allocatedPixmaps.end();
+ for ( ; aIt != aEnd; ++aIt )
+ if ( (*aIt)->page == req->pageNumber() && (*aIt)->observer == req->observer() )
+ {
+ AllocatedPixmap * p = *aIt;
+ m_allocatedPixmaps.erase( aIt );
+ m_allocatedPixmapsTotalMemory -= p->memory;
+ delete p;
+ break;
+ }
+
+ DocumentObserver *observer = req->observer();
+ if ( m_observers.contains(observer) )
+ {
+ // [MEM] 1.2 append memory allocation descriptor to the FIFO
+ qulonglong memoryBytes = 0;
+ const TilesManager *tm = req->d->tilesManager();
+ if ( tm )
+ memoryBytes = tm->totalMemory();
+ else
+ memoryBytes = 4 * req->width() * req->height();
- AllocatedPixmap * memoryPage = new AllocatedPixmap( req->observer(), req->pageNumber(), memoryBytes );
- m_allocatedPixmaps.append( memoryPage );
- m_allocatedPixmapsTotalMemory += memoryBytes;
+ AllocatedPixmap * memoryPage = new AllocatedPixmap( req->observer(), req->pageNumber(), memoryBytes );
+ m_allocatedPixmaps.append( memoryPage );
+ m_allocatedPixmapsTotalMemory += memoryBytes;
- // 2. notify an observer that its pixmap changed
- observer->notifyPageChanged( req->pageNumber(), DocumentObserver::Pixmap );
- }
+ // 2. notify an observer that its pixmap changed
+ observer->notifyPageChanged( req->pageNumber(), DocumentObserver::Pixmap );
+ }
#ifndef NDEBUG
- else
- qCWarning(OkularCoreDebug) << "Receiving a done request for the defunct observer" << observer;
+ else
+ qCWarning(OkularCoreDebug) << "Receiving a done request for the defunct observer" << observer;
#endif
+ }
// 3. delete request
m_pixmapRequestsMutex.lock();
diff --git a/core/document_p.h b/core/document_p.h
index 644a3c7..66e1d61 100644
--- a/core/document_p.h
+++ b/core/document_p.h
@@ -150,6 +150,7 @@ class DocumentPrivate
bool canModifyExternalAnnotations() const;
bool canRemoveExternalAnnotations() const;
OKULARCORE_EXPORT static QString docDataFileName(const QUrl &url, qint64 document_size);
+ bool cancelRenderingBecauseOf( PixmapRequest *executingRequest, PixmapRequest *newRequest );
// Methods that implement functionality needed by undo commands
void performAddPageAnnotation( int page, Annotation *annotation );
diff --git a/core/generator.cpp b/core/generator.cpp
index ba6013b..91e3444 100644
--- a/core/generator.cpp
+++ b/core/generator.cpp
@@ -22,6 +22,7 @@
#include <QtCore/QDebug>
#include <QIcon>
#include <QMimeDatabase>
+#include <QTimer>
#include <KLocalizedString>
#include <kwallet.h>
@@ -90,13 +91,14 @@ void GeneratorPrivate::pixmapGenerationFinished()
{
Q_Q( Generator );
PixmapRequest *request = mPixmapGenerationThread->request();
+ const QImage& img = mPixmapGenerationThread->image();
mPixmapGenerationThread->endGeneration();
QMutexLocker locker( threadsLock() );
- mPixmapReady = true;
if ( m_closing )
{
+ mPixmapReady = true;
delete request;
if ( mTextPageReady )
{
@@ -106,12 +108,25 @@ void GeneratorPrivate::pixmapGenerationFinished()
return;
}
- const QImage& img = mPixmapGenerationThread->image();
- request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( img ) ), request->normalizedRect() );
- const int pageNumber = request->page()->number();
- if ( mPixmapGenerationThread->calcBoundingBox() )
- q->updatePageBoundingBox( pageNumber, mPixmapGenerationThread->boundingBox() );
+ if ( !request->shouldAbortRender() )
+ {
+ request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( img ) ), request->normalizedRect() );
+ const int pageNumber = request->page()->number();
+
+ if ( mPixmapGenerationThread->calcBoundingBox() )
+ q->updatePageBoundingBox( pageNumber, mPixmapGenerationThread->boundingBox() );
+ }
+ else
+ {
+ // Cancel the text page generation too if it's still running
+ if ( mTextPageGenerationThread && mTextPageGenerationThread->isRunning() ) {
+ mTextPageGenerationThread->abortExtraction();
+ mTextPageGenerationThread->wait();
+ }
+ }
+
+ mPixmapReady = true;
q->signalPixmapRequestDone( request );
}
@@ -161,11 +176,10 @@ QImage GeneratorPrivate::image( PixmapRequest * )
}
-Generator::Generator(QObject* parent, const QVariantList&)
- : QObject(parent)
- , d_ptr( new GeneratorPrivate() )
+Generator::Generator(QObject* parent, const QVariantList &args)
+ : Generator( *new GeneratorPrivate(), parent, args )
{
- d_ptr->q_ptr = this;
+ // the delegated constructor does it all
}
Generator::Generator(GeneratorPrivate &dd, QObject *parent, const QVariantList &args)
@@ -253,6 +267,15 @@ void Generator::generatePixmap( PixmapRequest *request )
if ( request->asynchronous() && hasFeature( Threaded ) )
{
+ if ( d->textPageGenerationThread()->isFinished() && !canGenerateTextPage() )
+ {
+ // It can happen that the text generation has already finished but
+ // mTextPageReady is still false because textpageGenerationFinished
+ // didn't have time to run, if so queue ourselves
+ QTimer::singleShot(0, this, [this, request] { generatePixmap(request); });
+ return;
+ }
+
d->pixmapGenerationThread()->startGeneration( request, calcBoundingBox );
/**
@@ -261,8 +284,15 @@ void Generator::generatePixmap( PixmapRequest *request )
*/
if ( hasFeature( TextExtraction ) && !request->page()->hasTextPage() && canGenerateTextPage() && !d->m_closing ) {
d->mTextPageReady = false;
- // Queue the text generation request so that pixmap generation gets a chance to start before the text generation
- QMetaObject::invokeMethod(d->textPageGenerationThread(), "startGeneration", Qt::QueuedConnection, Q_ARG(Okular::Page*, request->page()));
+ d->textPageGenerationThread()->setPage( request->page() );
+
+ // dummy is used as a way to make sure the lambda gets disconnected each time it is executed
+ // since not all the times the pixmap generation thread starts we want the text generation thread to also start
+ QObject *dummy = new QObject();
+ connect(d_ptr->pixmapGenerationThread(), &QThread::started, dummy, [this, dummy] {
+ delete dummy;
+ d_ptr->textPageGenerationThread()->startGeneration();
+ });
}
return;
@@ -287,7 +317,8 @@ bool Generator::canGenerateTextPage() const
void Generator::generateTextPage( Page *page )
{
- TextPage *tp = textPage( page );
+ TextRequest treq( page );
+ TextPage *tp = textPage( &treq );
page->setTextPage( tp );
signalTextGenerationDone( page, tp );
}
@@ -298,7 +329,7 @@ QImage Generator::image( PixmapRequest *request )
return d->image( request );
}
-TextPage* Generator::textPage( Page* )
+TextPage* Generator::textPage( TextRequest * )
{
return nullptr;
}
@@ -413,7 +444,11 @@ void Generator::signalTextGenerationDone( Page *page, TextPage *textPage )
void Generator::signalPartialPixmapRequest( PixmapRequest *request, const QImage &image )
{
- request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( image ) ), request->normalizedRect() );
+ if ( request->shouldAbortRender() )
+ return;
+
+ PagePrivate *pagePrivate = PagePrivate::get( request->page() );
+ pagePrivate->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( image ) ), request->normalizedRect(), true /* isPartialPixmap */ );
const int pageNumber = request->page()->number();
request->observer()->notifyPageChanged( pageNumber, Okular::DocumentObserver::Pixmap );
@@ -504,6 +539,40 @@ QAbstractItemModel * Generator::layersModel() const
return nullptr;
}
+TextRequest::TextRequest()
+: d( new TextRequestPrivate )
+{
+ d->mPage = nullptr;
+ d->mShouldAbortExtraction = 0;
+}
+
+TextRequest::TextRequest( Page *page )
+: d( new TextRequestPrivate )
+{
+ d->mPage = page;
+ d->mShouldAbortExtraction = 0;
+}
+
+TextRequest::~TextRequest()
+{
+ delete d;
+}
+
+Page *TextRequest::page() const
+{
+ return d->mPage;
+}
+
+bool TextRequest::shouldAbortExtraction() const
+{
+ return d->mShouldAbortExtraction != 0;
+}
+
+TextRequestPrivate *TextRequestPrivate::get(const TextRequest *req)
+{
+ return req->d;
+}
+
PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features )
: d( new PixmapRequestPrivate )
{
@@ -517,6 +586,7 @@ PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int wi
d->mTile = false;
d->mNormalizedRect = NormalizedRect();
d->mPartialUpdatesWanted = false;
+ d->mShouldAbortRender = 0;
}
PixmapRequest::~PixmapRequest()
@@ -597,11 +667,21 @@ bool PixmapRequest::partialUpdatesWanted() const
return d->mPartialUpdatesWanted;
}
+bool PixmapRequest::shouldAbortRender() const
+{
+ return d->mShouldAbortRender != 0;
+}
+
Okular::TilesManager* PixmapRequestPrivate::tilesManager() const
{
return mPage->d->tilesManager(mObserver);
}
+PixmapRequestPrivate *PixmapRequestPrivate::get(const PixmapRequest *req)
+{
+ return req->d;
+}
+
void PixmapRequestPrivate::swap()
{
qSwap( mWidth, mHeight );
@@ -713,14 +793,21 @@ bool ExportFormat::operator!=( const ExportFormat &other ) const
QDebug operator<<( QDebug str, const Okular::PixmapRequest &req )
{
- QString s = QStringLiteral( "PixmapRequest(#%2, %1, %3x%4, page %6, prio %5)" )
- .arg( QString( req.asynchronous() ? QStringLiteral ( "async" ) : QStringLiteral ( "sync" ) ) )
- .arg( (qulonglong)req.observer() )
- .arg( req.width() )
- .arg( req.height() )
- .arg( req.priority() )
- .arg( req.pageNumber() );
- str << qPrintable( s );
+ PixmapRequestPrivate *reqPriv = PixmapRequestPrivate::get(&req);
+
+ str << "PixmapRequest:" << &req;
+ str << "- observer:" << (qulonglong)req.observer();
+ str << "- page:" << req.pageNumber();
+ str << "- width:" << req.width();
+ str << "- height:" << req.height();
+ str << "- priority:" << req.priority();
+ str << "- async:" << ( req.asynchronous() ? "true" : "false" );
+ str << "- tile:" << ( req.isTile() ? "true" : "false" );
+ str << "- rect:" << req.normalizedRect();
+ str << "- preload:" << ( req.preload() ? "true" : "false" );
+ str << "- partialUpdates:" << ( req.partialUpdatesWanted() ? "true" : "false" );
+ str << "- shouldAbort:" << ( req.shouldAbortRender() ? "true" : "false" );
+ str << "- force:" << ( reqPriv->mForce ? "true" : "false" );
return str;
}
diff --git a/core/generator.h b/core/generator.h
index 2443854..3146b6b 100644
--- a/core/generator.h
+++ b/core/generator.h
@@ -57,6 +57,8 @@ class Page;
class PixmapRequest;
class PixmapRequestPrivate;
class TextPage;
+class TextRequest;
+class TextRequestPrivate;
class NormalizedRect;
class SourceReference;
@@ -212,7 +214,8 @@ class OKULARCORE_EXPORT Generator : public QObject
PrintPostscript, ///< Whether the Generator supports postscript-based file printing.
PrintToFile, ///< Whether the Generator supports export to PDF & PS through the Print Dialog
TiledRendering, ///< Whether the Generator can render tiles @since 0.16 (KDE 4.10)
- SwapBackingFile ///< Whether the Generator can hot-swap the file it's reading from @since 1.3
+ SwapBackingFile, ///< Whether the Generator can hot-swap the file it's reading from @since 1.3
+ SupportsCancelling ///< Whether the Generator can cancel requests @since 1.4
};
/**
@@ -325,13 +328,11 @@ class OKULARCORE_EXPORT Generator : public QObject
* This method can be called to trigger the generation of
* a text page for the given @p page.
*
- * The generation is done synchronous or asynchronous, depending
- * on the @p type parameter and the capabilities of the
- * generator (e.g. multithreading).
+ * The generation is done in the calling thread.
*
* @see TextPage
*/
- virtual void generateTextPage( Page * page );
+ void generateTextPage( Page * page );
/**
* Returns the general information object of the document.
@@ -520,18 +521,24 @@ class OKULARCORE_EXPORT Generator : public QObject
* Returns the image of the page as specified in
* the passed pixmap @p request.
*
+ * Must return a null image if the request was cancelled and the generator supports cancelling
+ *
* @warning this method may be executed in its own separated thread if the
* @ref Threaded is enabled!
*/
virtual QImage image( PixmapRequest *page );
/**
- * Returns the text page for the given @p page.
+ * Returns the text page for the given @p request.
+ *
+ * Must return a null pointer if the request was cancelled and the generator supports cancelling
*
* @warning this method may be executed in its own separated thread if the
* @ref Threaded is enabled!
+ *
+ * @since 1.4
*/
- virtual TextPage* textPage( Page *page );
+ virtual TextPage* textPage( TextRequest *request );
/**
* Returns a pointer to the document.
@@ -749,6 +756,13 @@ class OKULARCORE_EXPORT PixmapRequest
*/
bool partialUpdatesWanted() const;
+ /**
+ * Should the request be aborted if possible?
+ *
+ * @since 1.4
+ */
+ bool shouldAbortRender() const;
+
private:
Q_DISABLE_COPY( PixmapRequest )
@@ -756,6 +770,43 @@ class OKULARCORE_EXPORT PixmapRequest
PixmapRequestPrivate* const d;
};
+/**
+ * @short Describes a text request.
+ *
+ * @since 1.4
+ */
+class OKULARCORE_EXPORT TextRequest
+{
+ public:
+ /**
+ * Creates a new text request.
+ */
+ TextRequest( Page *page );
+
+ TextRequest();
+
+ /**
+ * Destroys the pixmap request.
+ */
+ ~TextRequest();
+
+ /**
+ * Returns a pointer to the page where the pixmap shall be generated for.
+ */
+ Page *page() const;
+
+ /**
+ * Should the request be aborted if possible?
+ */
+ bool shouldAbortExtraction() const;
+
+ private:
+ Q_DISABLE_COPY( TextRequest )
+
+ friend TextRequestPrivate;
+ TextRequestPrivate* const d;
+};
+
}
Q_DECLARE_METATYPE(Okular::Generator::PrintError)
diff --git a/core/generator_p.cpp b/core/generator_p.cpp
index 502e2b5..f884bf3 100644
--- a/core/generator_p.cpp
+++ b/core/generator_p.cpp
@@ -42,7 +42,7 @@ PixmapRequest *PixmapGenerationThread::request() const
QImage PixmapGenerationThread::image() const
{
- return mImage;
+ return mRequest ? PixmapRequestPrivate::get(mRequest)->mResultImage : QImage();
}
bool PixmapGenerationThread::calcBoundingBox() const
@@ -57,37 +57,49 @@ NormalizedRect PixmapGenerationThread::boundingBox() const
void PixmapGenerationThread::run()
{
- mImage = QImage();
-
if ( mRequest )
{
- mImage = mGenerator->image( mRequest );
+ PixmapRequestPrivate::get(mRequest)->mResultImage = mGenerator->image( mRequest );
+
if ( mCalcBoundingBox )
- mBoundingBox = Utils::imageBoundingBox( &mImage );
+ mBoundingBox = Utils::imageBoundingBox( &PixmapRequestPrivate::get(mRequest)->mResultImage );
}
}
TextPageGenerationThread::TextPageGenerationThread( Generator *generator )
- : mGenerator( generator ), mPage( nullptr )
+ : mGenerator( generator ), mTextPage( nullptr )
{
+ TextRequestPrivate *treqPriv = TextRequestPrivate::get( &mTextRequest );
+ treqPriv->mPage = nullptr;
+ treqPriv->mShouldAbortExtraction = 0;
}
-void TextPageGenerationThread::startGeneration( Page *page )
+void TextPageGenerationThread::startGeneration()
{
- mPage = page;
-
- start( QThread::InheritPriority );
+ if ( page() )
+ {
+ start( QThread::InheritPriority );
+ }
}
void TextPageGenerationThread::endGeneration()
{
- mPage = nullptr;
+ TextRequestPrivate *treqPriv = TextRequestPrivate::get( &mTextRequest );
+ treqPriv->mPage = nullptr;
+ treqPriv->mShouldAbortExtraction = 0;
+}
+
+void TextPageGenerationThread::setPage( Page *page )
+{
+ TextRequestPrivate *treqPriv = TextRequestPrivate::get( &mTextRequest );
+ treqPriv->mPage = page;
+ treqPriv->mShouldAbortExtraction = 0;
}
Page *TextPageGenerationThread::page() const
{
- return mPage;
+ return mTextRequest.page();
}
TextPage* TextPageGenerationThread::textPage() const
@@ -95,12 +107,34 @@ TextPage* TextPageGenerationThread::textPage() const
return mTextPage;
}
+void TextPageGenerationThread::abortExtraction()
+{
+ // If extraction already finished no point in aborting
+ if ( !mTextPage )
+ {
+ TextRequestPrivate *treqPriv = TextRequestPrivate::get( &mTextRequest );
+ treqPriv->mShouldAbortExtraction = 1;
+ }
+}
+
+bool TextPageGenerationThread::shouldAbortExtraction() const
+{
+ return mTextRequest.shouldAbortExtraction();
+}
+
void TextPageGenerationThread::run()
{
mTextPage = nullptr;
- if ( mPage )
- mTextPage = mGenerator->textPage( mPage );
+ Q_ASSERT ( page() );
+
+ mTextPage = mGenerator->textPage( &mTextRequest );
+
+ if ( mTextRequest.shouldAbortExtraction() )
+ {
+ delete mTextPage;
+ mTextPage = nullptr;
+ }
}
diff --git a/core/generator_p.h b/core/generator_p.h
index 616e8fc..8e1f05e 100644
--- a/core/generator_p.h
+++ b/core/generator_p.h
@@ -22,6 +22,7 @@
class QEventLoop;
class QMutex;
+#include "generator.h"
#include "page.h"
namespace Okular {
@@ -80,6 +81,8 @@ class PixmapRequestPrivate
void swap();
TilesManager *tilesManager() const;
+ static PixmapRequestPrivate *get(const PixmapRequest *req);
+
DocumentObserver *mObserver;
int mPageNumber;
int mWidth;
@@ -91,6 +94,18 @@ class PixmapRequestPrivate
bool mPartialUpdatesWanted : 1;
Page *mPage;
NormalizedRect mNormalizedRect;
+ QAtomicInt mShouldAbortRender;
+ QImage mResultImage;
+};
+
+
+class TextRequestPrivate
+{
+ public:
+ static TextRequestPrivate *get(const TextRequest *req);
+
+ Page *mPage;
+ QAtomicInt mShouldAbortExtraction;
};
@@ -117,7 +132,6 @@ class PixmapGenerationThread : public QThread
private:
Generator *mGenerator;
PixmapRequest *mRequest;
- QImage mImage;
NormalizedRect mBoundingBox;
bool mCalcBoundingBox : 1;
};
@@ -132,20 +146,24 @@ class TextPageGenerationThread : public QThread
void endGeneration();
+ void setPage( Page *page );
Page *page() const;
TextPage* textPage() const;
+ void abortExtraction();
+ bool shouldAbortExtraction() const;
+
public slots:
- void startGeneration( Okular::Page *page );
+ void startGeneration();
protected:
void run() override;
private:
Generator *mGenerator;
- Page *mPage;
TextPage *mTextPage;
+ TextRequest mTextRequest;
};
class FontExtractionThread : public QThread
diff --git a/core/page.cpp b/core/page.cpp
index 3382798..c5ca06b 100644
--- a/core/page.cpp
+++ b/core/page.cpp
@@ -105,7 +105,7 @@ void PagePrivate::imageRotationDone( RotationJob * job )
if ( tm )
{
QPixmap *pixmap = new QPixmap( QPixmap::fromImage( job->image() ) );
- tm->setPixmap( pixmap, job->rect() );
+ tm->setPixmap( pixmap, job->rect(), job->isPartialUpdate() );
delete pixmap;
return;
}
@@ -531,34 +531,40 @@ QLinkedList< FormField * > Page::formFields() const
void Page::setPixmap( DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect )
{
- if ( d->m_rotation == Rotation0 ) {
- TilesManager *tm = d->tilesManager( observer );
+ d->setPixmap( observer, pixmap, rect, false /*isPartialPixmap*/ );
+}
+
+void PagePrivate::setPixmap( DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect, bool isPartialPixmap )
+{
+ if ( m_rotation == Rotation0 ) {
+ TilesManager *tm = tilesManager( observer );
if ( tm )
{
- tm->setPixmap( pixmap, rect );
+ tm->setPixmap( pixmap, rect, isPartialPixmap );
delete pixmap;
return;
}
- QMap< DocumentObserver*, PagePrivate::PixmapObject >::iterator it = d->m_pixmaps.find( observer );
- if ( it != d->m_pixmaps.end() )
+ QMap< DocumentObserver*, PagePrivate::PixmapObject >::iterator it = m_pixmaps.find( observer );
+ if ( it != m_pixmaps.end() )
{
delete it.value().m_pixmap;
}
else
{
- it = d->m_pixmaps.insert( observer, PagePrivate::PixmapObject() );
+ it = m_pixmaps.insert( observer, PagePrivate::PixmapObject() );
}
it.value().m_pixmap = pixmap;
- it.value().m_rotation = d->m_rotation;
+ it.value().m_rotation = m_rotation;
} else {
// it can happen that we get a setPixmap while closing and thus the page controller is gone
- if ( d->m_doc->m_pageController )
+ if ( 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);
+ RotationJob *job = new RotationJob( pixmap->toImage(), Rotation0, m_rotation, observer );
+ job->setPage( this );
+ job->setRect( TilesManager::toRotatedRect( rect, m_rotation ) );
+ job->setIsPartialUpdate( isPartialPixmap );
+ m_doc->m_pageController->addRotationJob(job);
}
delete pixmap;
diff --git a/core/page_p.h b/core/page_p.h
index 4b7831c..c456f48 100644
--- a/core/page_p.h
+++ b/core/page_p.h
@@ -134,10 +134,12 @@ class PagePrivate
*/
OKULARCORE_EXPORT static FormField *findEquivalentForm( const Page *p, FormField *oldField );
+ void setPixmap( DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect, bool isPartialPixmap );
+
class PixmapObject
{
public:
- QPixmap *m_pixmap;
+ QPixmap *m_pixmap = nullptr;
Rotation m_rotation;
};
QMap< DocumentObserver*, PixmapObject > m_pixmaps;
diff --git a/core/rotationjob.cpp b/core/rotationjob.cpp
index 519ebbf..d348624 100644
--- a/core/rotationjob.cpp
+++ b/core/rotationjob.cpp
@@ -17,6 +17,7 @@ RotationJob::RotationJob( const QImage &image, Rotation oldRotation, Rotation ne
: ThreadWeaver::QObjectDecorator( new RotationJobInternal( image, oldRotation, newRotation ) )
, mObserver( observer ), m_pd( nullptr )
, mRect( NormalizedRect() )
+ , mIsPartialUpdate( false )
{
}
@@ -30,6 +31,11 @@ void RotationJob::setRect( const NormalizedRect &rect )
mRect = rect;
}
+void RotationJob::setIsPartialUpdate( bool partialUpdate )
+{
+ mIsPartialUpdate = partialUpdate;
+}
+
DocumentObserver * RotationJob::observer() const
{
return mObserver;
@@ -45,6 +51,11 @@ NormalizedRect RotationJob::rect() const
return mRect;
}
+bool RotationJob::isPartialUpdate() const
+{
+ return mIsPartialUpdate;
+}
+
QTransform RotationJob::rotationMatrix( Rotation from, Rotation to )
{
QTransform matrix;
diff --git a/core/rotationjob_p.h b/core/rotationjob_p.h
index 51d0969..b0662f5 100644
--- a/core/rotationjob_p.h
+++ b/core/rotationjob_p.h
@@ -53,12 +53,14 @@ class RotationJob : public ThreadWeaver::QObjectDecorator
void setPage( PagePrivate * pd );
void setRect( const NormalizedRect &rect );
+ void setIsPartialUpdate( bool partialUpdate );
QImage image() const { return static_cast<const RotationJobInternal*>(job())->image(); }
Rotation rotation() const { return static_cast<const RotationJobInternal*>(job())->rotation(); }
DocumentObserver *observer() const;
PagePrivate * page() const;
NormalizedRect rect() const;
+ bool isPartialUpdate() const;
static QTransform rotationMatrix( Rotation from, Rotation to );
@@ -66,6 +68,7 @@ class RotationJob : public ThreadWeaver::QObjectDecorator
DocumentObserver *mObserver;
PagePrivate * m_pd;
NormalizedRect mRect;
+ bool mIsPartialUpdate;
};
}
diff --git a/core/textdocumentgenerator.cpp b/core/textdocumentgenerator.cpp
index 5fa60d7..78a5714 100644
--- a/core/textdocumentgenerator.cpp
+++ b/core/textdocumentgenerator.cpp
@@ -426,10 +426,10 @@ QImage TextDocumentGeneratorPrivate::image( PixmapRequest * request )
return image;
}
-Okular::TextPage* TextDocumentGenerator::textPage( Okular::Page * page )
+Okular::TextPage* TextDocumentGenerator::textPage( Okular::TextRequest * request )
{
Q_D( TextDocumentGenerator );
- return d->createTextPage( page->number() );
+ return d->createTextPage( request->page()->number() );
}
bool TextDocumentGenerator::print( QPrinter& printer )
diff --git a/core/textdocumentgenerator.h b/core/textdocumentgenerator.h
index 553b562..efcfcb7 100644
--- a/core/textdocumentgenerator.h
+++ b/core/textdocumentgenerator.h
@@ -208,7 +208,7 @@ class OKULARCORE_EXPORT TextDocumentGenerator : public Generator, public Okular:
protected:
bool doCloseDocument() override;
- Okular::TextPage* textPage( Okular::Page *page ) override;
+ Okular::TextPage* textPage( Okular::TextRequest *request ) override;
private:
Q_DECLARE_PRIVATE( TextDocumentGenerator )
diff --git a/core/tilesmanager.cpp b/core/tilesmanager.cpp
index e8282fa..d20c1b1 100644
--- a/core/tilesmanager.cpp
+++ b/core/tilesmanager.cpp
@@ -35,7 +35,7 @@ class TilesManager::Private
bool hasPixmap( const NormalizedRect &rect, const TileNode &tile ) const;
void tilesAt( const NormalizedRect &rect, TileNode &tile, QList<Tile> &result, TileLeaf tileLeaf );
- void setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile );
+ void setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile, bool isPartialPixmap );
/**
* Mark @p tile and all its children as dirty
@@ -185,42 +185,45 @@ void TilesManager::Private::markDirty( TileNode &tile )
}
}
-void TilesManager::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect )
+void TilesManager::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, bool isPartialPixmap )
{
- NormalizedRect rotatedRect = TilesManager::fromRotatedRect( rect, d->rotation );
+ const NormalizedRect rotatedRect = TilesManager::fromRotatedRect( rect, d->rotation );
if ( !d->requestRect.isNull() )
{
if ( !(d->requestRect == rect) )
return;
- // Check whether the pixmap has the same absolute size of the expected
- // request.
- // If the document is rotated, rotate requestRect back to the original
- // rotation before comparing to pixmap's size. This is to avoid
- // conversion issues. The pixmap request was made using an unrotated
- // rect.
- QSize pixmapSize = pixmap->size();
- int w = width();
- int h = height();
- if ( d->rotation % 2 )
+ if ( pixmap )
{
- qSwap(w, h);
- pixmapSize.transpose();
- }
+ // Check whether the pixmap has the same absolute size of the expected
+ // request.
+ // If the document is rotated, rotate requestRect back to the original
+ // rotation before comparing to pixmap's size. This is to avoid
+ // conversion issues. The pixmap request was made using an unrotated
+ // rect.
+ QSize pixmapSize = pixmap->size();
+ int w = width();
+ int h = height();
+ if ( d->rotation % 2 )
+ {
+ qSwap(w, h);
+ pixmapSize.transpose();
+ }
- if ( rotatedRect.geometry( w, h ).size() != pixmapSize )
- return;
+ if ( rotatedRect.geometry( w, h ).size() != pixmapSize )
+ return;
+ }
d->requestRect = NormalizedRect();
}
for ( int i = 0; i < 16; ++i )
{
- d->setPixmap( pixmap, rotatedRect, d->tiles[ i ] );
+ d->setPixmap( pixmap, rotatedRect, d->tiles[ i ], isPartialPixmap );
}
}
-void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile )
+void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile, bool isPartialPixmap )
{
QRect pixmapRect = TilesManager::toRotatedRect( rect, rotation ).geometry( width, height );
@@ -236,7 +239,7 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
if ( tile.nTiles > 0 )
{
for ( int i = 0; i < tile.nTiles; ++i )
- setPixmap( pixmap, rect, tile.tiles[ i ] );
+ setPixmap( pixmap, rect, tile.tiles[ i ], isPartialPixmap );
delete tile.pixmap;
tile.pixmap = nullptr;
@@ -248,7 +251,7 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
// the tile lies entirely within the viewport
if ( tile.nTiles == 0 )
{
- tile.dirty = false;
+ tile.dirty = isPartialPixmap;
// check whether the tile size is big and split it if necessary
if ( !splitBigTiles( tile, rect ) )
@@ -258,10 +261,17 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
delete tile.pixmap;
}
- NormalizedRect rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation );
- tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) );
tile.rotation = rotation;
- totalPixels += tile.pixmap->width()*tile.pixmap->height();
+ if ( pixmap )
+ {
+ const NormalizedRect rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation );
+ tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) );
+ totalPixels += tile.pixmap->width()*tile.pixmap->height();
+ }
+ else
+ {
+ tile.pixmap = nullptr;
+ }
}
else
{
@@ -273,7 +283,7 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
}
for ( int i = 0; i < tile.nTiles; ++i )
- setPixmap( pixmap, rect, tile.tiles[ i ] );
+ setPixmap( pixmap, rect, tile.tiles[ i ], isPartialPixmap );
}
}
else
@@ -283,7 +293,7 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
// small, discards the children tiles and use the current one
if ( tileRect.width()*tileRect.height() >= TILES_MAXSIZE )
{
- tile.dirty = false;
+ tile.dirty = isPartialPixmap;
if ( tile.pixmap )
{
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
@@ -292,7 +302,7 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
}
for ( int i = 0; i < tile.nTiles; ++i )
- setPixmap( pixmap, rect, tile.tiles[ i ] );
+ setPixmap( pixmap, rect, tile.tiles[ i ], isPartialPixmap );
}
else
{
@@ -313,11 +323,18 @@ void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRe
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
delete tile.pixmap;
}
- NormalizedRect rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation );
- tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) );
tile.rotation = rotation;
- totalPixels += tile.pixmap->width()*tile.pixmap->height();
- tile.dirty = false;
+ if ( pixmap )
+ {
+ const NormalizedRect rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation );
+ tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) );
+ totalPixels += tile.pixmap->width()*tile.pixmap->height();
+ }
+ else
+ {
+ tile.pixmap = nullptr;
+ }
+ tile.dirty = isPartialPixmap;
}
}
}
@@ -336,7 +353,8 @@ bool TilesManager::hasPixmap( const NormalizedRect &rect )
bool TilesManager::Private::hasPixmap( const NormalizedRect &rect, const TileNode &tile ) const
{
- if ( !tile.rect.intersects( rect ) )
+ const NormalizedRect rectIntersection = tile.rect & rect;
+ if ( rectIntersection.width() <= 0 || rectIntersection.height() <= 0 )
return true;
if ( tile.nTiles == 0 )
diff --git a/core/tilesmanager_p.h b/core/tilesmanager_p.h
index 44aa78a..a1acde6 100644
--- a/core/tilesmanager_p.h
+++ b/core/tilesmanager_p.h
@@ -116,7 +116,7 @@ class TilesManager
* Also it checks the dimensions of the given parameters against the
* current request as to avoid setting pixmaps of late requests.
*/
- void setPixmap( const QPixmap *pixmap, const NormalizedRect &rect );
+ void setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, bool isPartialPixmap );
/**
* Checks whether all tiles intersecting with @p rect are available.
diff --git a/generators/chm/generator_chm.cpp b/generators/chm/generator_chm.cpp
index ada9b3d..3befb51 100644
--- a/generators/chm/generator_chm.cpp
+++ b/generators/chm/generator_chm.cpp
@@ -408,10 +408,11 @@ void CHMGenerator::additionalRequestData()
}
}
-Okular::TextPage* CHMGenerator::textPage( Okular::Page * page )
+Okular::TextPage* CHMGenerator::textPage( Okular::TextRequest * request )
{
userMutex()->lock();
+ const Okular::Page *page = request->page();
m_syncGen->view()->resize(page->width(), page->height());
preparePageForSyncOperation(m_pageUrl[page->number()]);
diff --git a/generators/chm/generator_chm.h b/generators/chm/generator_chm.h
index 25295bf..13fa710 100644
--- a/generators/chm/generator_chm.h
+++ b/generators/chm/generator_chm.h
@@ -50,7 +50,7 @@ class CHMGenerator : public Okular::Generator
protected:
bool doCloseDocument() override;
- Okular::TextPage* textPage( Okular::Page *page ) override;
+ Okular::TextPage* textPage( Okular::TextRequest *request ) override;
private:
void additionalRequestData();
diff --git a/generators/djvu/generator_djvu.cpp b/generators/djvu/generator_djvu.cpp
index 1ddacfd..644f746 100644
--- a/generators/djvu/generator_djvu.cpp
+++ b/generators/djvu/generator_djvu.cpp
@@ -206,9 +206,10 @@ QVariant DjVuGenerator::metaData( const QString &key, const QVariant &option ) c
return QVariant();
}
-Okular::TextPage* DjVuGenerator::textPage( Okular::Page *page )
+Okular::TextPage* DjVuGenerator::textPage( Okular::TextRequest *request )
{
userMutex()->lock();
+ const Okular::Page *page = request->page();
QList<KDjVu::TextEntity> te;
#if 0
m_djvu->textEntities( page->number(), "char" );
diff --git a/generators/djvu/generator_djvu.h b/generators/djvu/generator_djvu.h
index c5d3970..4e11b5c 100644
--- a/generators/djvu/generator_djvu.h
+++ b/generators/djvu/generator_djvu.h
@@ -43,7 +43,7 @@ class DjVuGenerator : public Okular::Generator
bool doCloseDocument() override;
// pixmap generation
QImage image( Okular::PixmapRequest *request ) override;
- Okular::TextPage* textPage( Okular::Page *page ) override;
+ Okular::TextPage* textPage( Okular::TextRequest *request ) override;
private:
void loadPages( QVector<Okular::Page*> & pagesVector, int rotation );
diff --git a/generators/dvi/generator_dvi.cpp b/generators/dvi/generator_dvi.cpp
index 0815188..16a64f9 100644
--- a/generators/dvi/generator_dvi.cpp
+++ b/generators/dvi/generator_dvi.cpp
@@ -247,8 +247,10 @@ QImage DviGenerator::image( Okular::PixmapRequest *request )
return ret;
}
-Okular::TextPage* DviGenerator::textPage( Okular::Page *page )
+Okular::TextPage* DviGenerator::textPage( Okular::TextRequest *request )
{
+ const Okular::Page *page = request->page();
+
qCDebug(OkularDviDebug);
dviPageInfo *pageInfo = new dviPageInfo();
pageSize ps;
diff --git a/generators/dvi/generator_dvi.h b/generators/dvi/generator_dvi.h
index fa4ebdf..3f7b4f8 100644
--- a/generators/dvi/generator_dvi.h
+++ b/generators/dvi/generator_dvi.h
@@ -47,7 +47,7 @@ class DviGenerator : public Okular::Generator
protected:
bool doCloseDocument() override;
QImage image( Okular::PixmapRequest * request ) override;
- Okular::TextPage* textPage( Okular::Page *page ) override;
+ Okular::TextPage* textPage( Okular::TextRequest *request ) override;
private:
double m_resolution;
diff --git a/generators/poppler/CMakeLists.txt b/generators/poppler/CMakeLists.txt
index a5ad027..a3dd38d 100644
--- a/generators/poppler/CMakeLists.txt
+++ b/generators/poppler/CMakeLists.txt
@@ -61,6 +61,17 @@ int main()
}
" HAVE_POPPLER_0_62)
+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, nullptr, QVariant());
+ return 0;
+}
+" HAVE_POPPLER_0_63)
+
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
index 9b8da50..16831f7 100644
--- a/generators/poppler/config-okular-poppler.h.cmake
+++ b/generators/poppler/config-okular-poppler.h.cmake
@@ -21,3 +21,6 @@
/* Defined if we have the 0.62 version of the Poppler library */
#cmakedefine HAVE_POPPLER_0_62 1
+
+/* Defined if we have the 0.63 version of the Poppler library */
+#cmakedefine HAVE_POPPLER_0_63 1
diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp
index 61a393f..5421c74 100644
--- a/generators/poppler/generator_pdf.cpp
+++ b/generators/poppler/generator_pdf.cpp
@@ -517,6 +517,9 @@ PDFGenerator::PDFGenerator( QObject *parent, const QVariantList &args )
setFeature( ReadRawData );
setFeature( TiledRendering );
setFeature( SwapBackingFile );
+#ifdef HAVE_POPPLER_0_63
+ setFeature( SupportsCancelling );
+#endif
// You only need to do it once not for each of the documents but it is cheap enough
// so doing it all the time won't hurt either
@@ -910,9 +913,9 @@ bool PDFGenerator::isAllowed( Okular::Permission permission ) const
}
#ifdef HAVE_POPPLER_0_62
-struct PartialUpdatePayload
+struct RenderImagePayload
{
- PartialUpdatePayload(PDFGenerator *g, Okular::PixmapRequest *r) :
+ RenderImagePayload(PDFGenerator *g, Okular::PixmapRequest *r) :
generator(g), request(r)
{
// Don't report partial updates for the first 500 ms
@@ -925,11 +928,11 @@ struct PartialUpdatePayload
Okular::PixmapRequest *request;
QTimer timer;
};
-Q_DECLARE_METATYPE(PartialUpdatePayload*)
+Q_DECLARE_METATYPE(RenderImagePayload*)
static bool shouldDoPartialUpdateCallback(const QVariant &vPayload)
{
- auto payload = vPayload.value<PartialUpdatePayload *>();
+ auto payload = vPayload.value<RenderImagePayload *>();
// Since the timer lives in a thread without an event loop we need to stop it ourselves
// when the remaining time has reached 0
@@ -942,11 +945,19 @@ static bool shouldDoPartialUpdateCallback(const QVariant &vPayload)
static void partialUpdateCallback(const QImage &image, const QVariant &vPayload)
{
- auto payload = vPayload.value<PartialUpdatePayload *>();
+ auto payload = vPayload.value<RenderImagePayload *>();
QMetaObject::invokeMethod(payload->generator, "signalPartialPixmapRequest", Qt::QueuedConnection, Q_ARG(Okular::PixmapRequest*, payload->request), Q_ARG(QImage, image));
}
#endif
+#ifdef HAVE_POPPLER_0_63
+static bool shouldAbortRenderCallback(const QVariant &vPayload)
+{
+ auto payload = vPayload.value<RenderImagePayload *>();
+ return payload->request->shouldAbortRender();
+}
+#endif
+
QImage PDFGenerator::image( Okular::PixmapRequest * request )
{
// debug requests to this (xpdf) generator
@@ -970,6 +981,12 @@ QImage PDFGenerator::image( Okular::PixmapRequest * request )
// 0. LOCK [waits for the thread end]
userMutex()->lock();
+ if ( request->shouldAbortRender() )
+ {
+ userMutex()->unlock();
+ return QImage();
+ }
+
// 1. Set OutputDev parameters and Generate contents
// note: thread safety is set on 'false' for the GUI (this) thread
Poppler::Page *p = pdfdoc->page(page->number());
@@ -978,37 +995,79 @@ QImage PDFGenerator::image( Okular::PixmapRequest * request )
QImage img;
if (p)
{
+#ifdef HAVE_POPPLER_0_63
if ( request->isTile() )
{
- QRect rect = request->normalizedRect().geometry( request->width(), request->height() );
-#ifdef HAVE_POPPLER_0_62
+ const QRect rect = request->normalizedRect().geometry( request->width(), request->height() );
if ( request->partialUpdatesWanted() )
{
- PartialUpdatePayload payload( this, request );
+ RenderImagePayload payload( this, request );
+ img = p->renderToImage( fakeDpiX, fakeDpiY, rect.x(), rect.y(), rect.width(), rect.height(), Poppler::Page::Rotate0,
+ partialUpdateCallback, shouldDoPartialUpdateCallback, shouldAbortRenderCallback, QVariant::fromValue( &payload ) );
+ }
+ else
+ {
+ RenderImagePayload payload( this, request );
+ img = p->renderToImage( fakeDpiX, fakeDpiY, rect.x(), rect.y(), rect.width(), rect.height(), Poppler::Page::Rotate0,
+ nullptr, nullptr, shouldAbortRenderCallback, QVariant::fromValue( &payload ) );
+ }
+ }
+ else
+ {
+ if ( request->partialUpdatesWanted() )
+ {
+ RenderImagePayload payload( this, request );
+ img = p->renderToImage( fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0,
+ partialUpdateCallback, shouldDoPartialUpdateCallback, shouldAbortRenderCallback, QVariant::fromValue( &payload ) );
+ }
+ else
+ {
+ RenderImagePayload payload( this, request );
+ img = p->renderToImage( fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0,
+ nullptr, nullptr, shouldAbortRenderCallback, QVariant::fromValue( &payload ) );
+ }
+
+ }
+#elif defined(HAVE_POPPLER_0_62)
+ if ( request->isTile() )
+ {
+ const QRect rect = request->normalizedRect().geometry( request->width(), request->height() );
+ if ( request->partialUpdatesWanted() )
+ {
+ RenderImagePayload 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
{
-#ifdef HAVE_POPPLER_0_62
if ( request->partialUpdatesWanted() )
{
- PartialUpdatePayload payload(this, request);
+ RenderImagePayload 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 );
+ img = p->renderToImage( fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 );
}
+
}
+#else
+ if ( request->isTile() )
+ {
+ const 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 );
+ }
+ else
+ {
+ img = p->renderToImage( fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 );
+ }
+#endif
}
else
{
@@ -1100,8 +1159,28 @@ void PDFGenerator::resolveMediaLinkReferences( Okular::Page *page )
resolveMediaLinkReference( field->activationAction() );
}
-Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
+#ifdef HAVE_POPPLER_0_63
+struct TextExtractionPayload
{
+ TextExtractionPayload(Okular::TextRequest *r) :
+ request(r)
+ {
+ }
+
+ Okular::TextRequest *request;
+};
+Q_DECLARE_METATYPE(TextExtractionPayload*)
+
+static bool shouldAbortTextExtractionCallback(const QVariant &vPayload)
+{
+ auto payload = vPayload.value<TextExtractionPayload *>();
+ return payload->request->shouldAbortExtraction();
+}
+#endif
+
+Okular::TextPage* PDFGenerator::textPage( Okular::TextRequest *request )
+{
+ const Okular::Page *page = request->page();
#ifdef PDFGENERATOR_DEBUG
qCDebug(OkularPdfDebug) << "page" << page->number();
#endif
@@ -1112,7 +1191,12 @@ Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
if (pp)
{
userMutex()->lock();
+#ifdef HAVE_POPPLER_0_63
+ TextExtractionPayload payload(request);
+ textList = pp->textList( Poppler::Page::Rotate0, shouldAbortTextExtractionCallback, QVariant::fromValue( &payload ) );
+#else
textList = pp->textList();
+#endif
userMutex()->unlock();
QSizeF s = pp->pageSizeF();
@@ -1127,6 +1211,9 @@ Okular::TextPage* PDFGenerator::textPage( Okular::Page *page )
pageHeight = defaultPageHeight;
}
+ if ( textList.isEmpty() && request->shouldAbortExtraction() )
+ return nullptr;
+
Okular::TextPage *tp = abstractTextPage(textList, pageHeight, pageWidth, (Poppler::Page::Rotation)page->orientation());
qDeleteAll(textList);
return tp;
diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h
index d6ea065..4439a11 100644
--- a/generators/poppler/generator_pdf.h
+++ b/generators/poppler/generator_pdf.h
@@ -104,7 +104,7 @@ class PDFGenerator : public Okular::Generator, public Okular::ConfigInterface, p
protected:
SwapBackingFileResult swapBackingFile( QString const &newFileName, QVector<Okular::Page*> & newPagesVector ) override;
bool doCloseDocument() override;
- Okular::TextPage* textPage( Okular::Page *page ) override;
+ Okular::TextPage* textPage( Okular::TextRequest *request ) override;
protected Q_SLOTS:
void requestFontData(const Okular::FontInfo &font, QByteArray *data);
diff --git a/generators/xps/generator_xps.cpp b/generators/xps/generator_xps.cpp
index 69b5079..d184b68 100644
--- a/generators/xps/generator_xps.cpp
+++ b/generators/xps/generator_xps.cpp
@@ -2117,10 +2117,10 @@ QImage XpsGenerator::image( Okular::PixmapRequest * request )
return image;
}
-Okular::TextPage* XpsGenerator::textPage( Okular::Page * page )
+Okular::TextPage* XpsGenerator::textPage( Okular::TextRequest * request )
{
QMutexLocker lock( userMutex() );
- XpsPage * xpsPage = m_xpsFile->page( page->number() );
+ XpsPage * xpsPage = m_xpsFile->page( request->page()->number() );
return xpsPage->textPage();
}
diff --git a/generators/xps/generator_xps.h b/generators/xps/generator_xps.h
index 93e80a5..3535fb4 100644
--- a/generators/xps/generator_xps.h
+++ b/generators/xps/generator_xps.h
@@ -319,7 +319,7 @@ class XpsGenerator : public Okular::Generator
protected:
bool doCloseDocument() override;
QImage image( Okular::PixmapRequest *page ) override;
- Okular::TextPage* textPage( Okular::Page * page ) override;
+ Okular::TextPage* textPage( Okular::TextRequest * request ) override;
private:
XpsFile *m_xpsFile;