okular: remove unused tiles feature

it was supposed to be optimization for extreme zoom-out levels

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-12-17 00:11:56 +02:00
parent 8f8e8671b4
commit 306b8bc92e
17 changed files with 116 additions and 1469 deletions

View file

@ -99,7 +99,6 @@ set(okularcore_SRCS
core/textdocumentgenerator.cpp
core/textdocumentsettings.cpp
core/textpage.cpp
core/tilesmanager.cpp
core/utils.cpp
core/view.cpp
core/chooseenginewidget.ui
@ -124,7 +123,6 @@ install(
core/textdocumentgenerator.h
core/textdocumentsettings.h
core/textpage.h
core/tile.h
core/utils.h
core/version.h
core/observer.h

View file

@ -491,3 +491,53 @@ bool SourceRefObjectRect::contains( double x, double y, double xScale, double yS
{
return distanceSqr( x, y, xScale, yScale ) < ( pow( 7.0 / xScale, 2 ) + pow( 7.0 / yScale, 2 ) );
}
NormalizedRect NormalizedRect::toRotatedRect( const NormalizedRect &rect, Rotation rotation )
{
if ( rotation == Rotation0 )
return rect;
NormalizedRect newRect;
switch ( rotation )
{
case Rotation90:
newRect = NormalizedRect( 1 - rect.bottom, rect.left, 1 - rect.top, rect.right );
break;
case Rotation180:
newRect = NormalizedRect( 1 - rect.right, 1 - rect.bottom, 1 - rect.left, 1 - rect.top );
break;
case Rotation270:
newRect = NormalizedRect( rect.top, 1 - rect.right, rect.bottom, 1 - rect.left );
break;
default:
newRect = rect;
break;
}
return newRect;
}
NormalizedRect NormalizedRect::fromRotatedRect( const NormalizedRect &rect, Rotation rotation )
{
if ( rotation == Rotation0 )
return rect;
NormalizedRect newRect;
switch ( rotation )
{
case Rotation90:
newRect = NormalizedRect( rect.top, 1 - rect.right, rect.bottom, 1 - rect.left );
break;
case Rotation180:
newRect = NormalizedRect( 1 - rect.right, 1 - rect.bottom, 1 - rect.left, 1 - rect.top );
break;
case Rotation270:
newRect = NormalizedRect( 1 - rect.bottom, rect.left, 1 - rect.top, rect.right );
break;
default:
newRect = rect;
break;
}
return newRect;
}

View file

@ -299,6 +299,16 @@ class OKULAR_EXPORT NormalizedRect
return pow( distX * xScale, 2 ) + pow( distY * yScale, 2 );
}
/**
* Returns a rotated NormalizedRect given a @p rotation
*/
static NormalizedRect toRotatedRect( const NormalizedRect &rect, Rotation rotation );
/**
* Returns a non rotated version of @p rect, which is rotated by @p rotation
*/
static NormalizedRect fromRotatedRect( const NormalizedRect &rect, Rotation rotation );
/**
* The normalized left coordinate.
*/

View file

@ -76,8 +76,6 @@
#include "sourcereference.h"
#include "sourcereference_p.h"
#include "texteditors_p.h"
#include "tile.h"
#include "tilesmanager_p.h"
#include "utils_p.h"
#include "view.h"
#include "view_p.h"
@ -373,8 +371,6 @@ void DocumentPrivate::cleanupPixmapMemory( qulonglong memoryToFree )
delete p;
}
// If we're still on low memory, try to free individual tiles
// Store pages that weren't completely removed
QList< AllocatedPixmap * > pixmapsToKeep;
@ -388,30 +384,7 @@ void DocumentPrivate::cleanupPixmapMemory( qulonglong memoryToFree )
continue;
clean_hits++;
TilesManager *tilesManager = m_pagesVector.at( p->page )->d->tilesManager( observer );
if ( tilesManager && tilesManager->totalMemory() > 0 )
{
qulonglong memoryDiff = p->memory;
NormalizedRect visibleRect;
if ( visibleRects.contains( p->page ) )
visibleRect = visibleRects[ p->page ]->rect;
// Free non visible tiles
tilesManager->cleanupPixmapMemory( memoryToFree, visibleRect, currentViewportPage );
p->memory = tilesManager->totalMemory();
memoryDiff -= p->memory;
memoryToFree = (memoryDiff < memoryToFree) ? (memoryToFree - memoryDiff) : 0;
m_allocatedPixmapsTotalMemory -= memoryDiff;
if ( p->memory > 0 )
pixmapsToKeep.append( p );
else
delete p;
}
else
pixmapsToKeep.append( p );
pixmapsToKeep.append( p );
}
if (clean_hits == 0) break;
@ -1301,8 +1274,7 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
continue;
}
QRect requestRect = r->isTile() ? r->normalizedRect().geometry( r->width(), r->height() ) : QRect( 0, 0, r->width(), r->height() );
TilesManager *tilesManager = r->d->tilesManager();
QRect requestRect = QRect( 0, 0, r->width(), r->height() );
// If it's a preload but the generator is not threaded no point in trying to preload
if ( r->preload() && !m_generator->hasFeature( Generator::Threaded ) )
@ -1323,80 +1295,6 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
//kDebug() << "Ignoring request that doesn't fit in cache";
delete r;
}
// Ignore requests for pixmaps that are already being generated
else if ( tilesManager && tilesManager->isRequesting( r->normalizedRect(), r->width(), r->height() ) )
{
m_pixmapRequestsStack.pop_back();
delete r;
}
// If the requested area is above 8000000 pixels, switch on the tile manager
else if ( !tilesManager && m_generator->hasFeature( Generator::TiledRendering ) && (long)r->width() * (long)r->height() > 8000000L )
{
// if the image is too big. start using tiles
kDebug(OkularDebug).nospace() << "Start using tiles on page " << r->pageNumber()
<< " (" << r->width() << "x" << r->height() << " px);";
// fill the tiles manager with the last rendered pixmap
const QPixmap *pixmap = r->page()->_o_nearestPixmap( r->observer(), r->width(), r->height() );
if ( pixmap )
{
tilesManager = new TilesManager( r->pageNumber(), pixmap->width(), pixmap->height(), r->page()->rotation() );
tilesManager->setPixmap( pixmap, NormalizedRect( 0, 0, 1, 1 ) );
tilesManager->setSize( r->width(), r->height() );
}
else
{
// create new tiles manager
tilesManager = new TilesManager( r->pageNumber(), r->width(), r->height(), r->page()->rotation() );
}
tilesManager->setRequest( r->normalizedRect(), r->width(), r->height() );
r->page()->deletePixmap( r->observer() );
r->page()->d->setTilesManager( r->observer(), tilesManager );
r->setTile( true );
// Change normalizedRect to the smallest rect that contains all
// visible tiles.
if ( !r->normalizedRect().isNull() )
{
NormalizedRect tilesRect;
const QList<Tile> tiles = tilesManager->tilesAt( r->normalizedRect(), TilesManager::TerminalTile );
QList<Tile>::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd();
while ( tIt != tEnd )
{
Tile tile = *tIt;
if ( tilesRect.isNull() )
tilesRect = tile.rect();
else
tilesRect |= tile.rect();
++tIt;
}
r->setNormalizedRect( tilesRect );
request = r;
}
else
{
// Discard request if normalizedRect is null. This happens in
// preload requests issued by PageView if the requested page is
// not visible and the user has just switched from a non-tiled
// zoom level to a tiled one
m_pixmapRequestsStack.pop_back();
delete r;
}
}
// If the requested area is below 6000000 pixels, switch off the tile manager
else if ( tilesManager && (long)r->width() * (long)r->height() < 6000000L )
{
kDebug(OkularDebug).nospace() << "Stop using tiles on page " << r->pageNumber()
<< " (" << r->width() << "x" << r->height() << " px);";
// page is too small. stop using tiles.
r->page()->deletePixmap( r->observer() );
r->setTile( false );
request = r;
}
else if ( (long)requestRect.width() * (long)requestRect.height() > 20000000L )
{
m_pixmapRequestsStack.pop_back();
@ -1423,12 +1321,7 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
}
// [MEM] preventive memory freeing
qulonglong pixmapBytes = 0;
TilesManager * tm = request->d->tilesManager();
if ( tm )
pixmapBytes = tm->totalMemory();
else
pixmapBytes = 4 * request->width() * request->height();
qulonglong pixmapBytes = 4 * request->width() * request->height();
if ( pixmapBytes > (1024 * 1024) )
cleanupPixmapMemory( memoryToFree /* previously calculated value */ );
@ -1436,19 +1329,15 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
// submit the request to the generator
if ( m_generator->canGeneratePixmap() )
{
QRect requestRect = !request->isTile() ? QRect(0, 0, request->width(), request->height() ) : request->normalizedRect().geometry( request->width(), request->height() );
kDebug(OkularDebug).nospace() << "sending request observer=" << request->observer() << " " <<requestRect.width() << "x" << requestRect.height() << "@" << request->pageNumber() << " async == " << request->asynchronous() << " isTile == " << request->isTile();
QRect requestRect = QRect(0, 0, request->width(), request->height() );
kDebug(OkularDebug).nospace() << "sending request observer=" << request->observer() << " " <<requestRect.width() << "x" << requestRect.height() << "@" << request->pageNumber() << " async == " << request->asynchronous();
m_pixmapRequestsStack.removeAll ( request );
if ( tm )
tm->setRequest( request->normalizedRect(), request->width(), request->height() );
if ( (int)m_rotation % 2 )
request->d->swap();
if ( m_rotation != Rotation0 && !request->normalizedRect().isNull() )
request->setNormalizedRect( TilesManager::fromRotatedRect(
request->normalizedRect(), m_rotation ) );
request->setNormalizedRect( NormalizedRect::fromRotatedRect(request->normalizedRect(), m_rotation ) );
// we always have to unlock _before_ the generatePixmap() because
// a sync generation would end with requestDone() -> deadlock, and
@ -1553,43 +1442,6 @@ void DocumentPrivate::refreshPixmaps( int pageNumber )
requestedPixmaps.push_back( p );
}
foreach (DocumentObserver *observer, m_observers)
{
TilesManager *tilesManager = page->d->tilesManager( observer );
if ( tilesManager )
{
tilesManager->markDirty();
PixmapRequest * p = new PixmapRequest( observer, pageNumber, tilesManager->width(), tilesManager->height(), 1, PixmapRequest::Asynchronous );
NormalizedRect tilesRect;
// Get the visible page rect
NormalizedRect visibleRect;
QVector< Okular::VisiblePageRect * >::const_iterator vIt = m_pageRects.constBegin(), vEnd = m_pageRects.constEnd();
for ( ; vIt != vEnd; ++vIt )
{
if ( (*vIt)->pageNumber == pageNumber )
{
visibleRect = (*vIt)->rect;
break;
}
}
if ( !visibleRect.isNull() )
{
p->setNormalizedRect( visibleRect );
p->setTile( true );
p->d->mForce = true;
requestedPixmaps.push_back( p );
}
else
{
delete p;
}
}
}
if ( !requestedPixmaps.isEmpty() )
m_parent->requestPixmaps( requestedPixmaps, Okular::Document::NoOption );
}
@ -2688,11 +2540,6 @@ bool Document::supportsPageSizes() const
return d->m_generator ? d->m_generator->hasFeature( Generator::PageSizes ) : false;
}
bool Document::supportsTiles() const
{
return d->m_generator ? d->m_generator->hasFeature( Generator::TiledRendering ) : false;
}
PageSize::List Document::pageSizes() const
{
if ( d->m_generator )
@ -2850,30 +2697,6 @@ void Document::requestPixmaps( const QList< PixmapRequest * > & requests, Pixmap
request->d->mPage = d->m_pagesVector.value( request->pageNumber() );
if ( request->isTile() )
{
// Change the current request rect so that only invalid tiles are
// requested. Also make sure the rect is tile-aligned.
NormalizedRect tilesRect;
const QList<Tile> tiles = request->d->tilesManager()->tilesAt( request->normalizedRect(), TilesManager::TerminalTile );
QList<Tile>::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd();
while ( tIt != tEnd )
{
const Tile &tile = *tIt;
if ( !tile.isValid() )
{
if ( tilesRect.isNull() )
tilesRect = tile.rect();
else
tilesRect |= tile.rect();
}
tIt++;
}
request->setNormalizedRect( tilesRect );
}
if ( !request->asynchronous() )
request->d->mPriority = 0;
@ -4301,12 +4124,7 @@ void DocumentPrivate::requestDone( PixmapRequest * req )
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();
qulonglong memoryBytes = 4 * req->width() * req->height();
AllocatedPixmap * memoryPage = new AllocatedPixmap( req->observer(), req->pageNumber(), memoryBytes );
m_allocatedPixmaps.append( memoryPage );

View file

@ -229,13 +229,6 @@ class OKULAR_EXPORT Document : public QObject
*/
bool supportsPageSizes() const;
/**
* Returns whether the current document supports tiles
*
* @since 0.16 (KDE 4.10)
*/
bool supportsTiles() const;
/**
* Returns the list of supported page sizes or an empty list if this
* feature is not available.

View file

@ -236,7 +236,7 @@ void Generator::generatePixmap( PixmapRequest *request )
Q_D( Generator );
d->mPixmapReady = false;
const bool calcBoundingBox = !request->isTile() && !request->page()->isBoundingBoxKnown();
const bool calcBoundingBox = !request->page()->isBoundingBoxKnown();
if ( request->asynchronous() && hasFeature( Threaded ) )
{
@ -467,7 +467,6 @@ PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int wi
d->mPriority = priority;
d->mFeatures = features;
d->mForce = false;
d->mTile = false;
d->mNormalizedRect = NormalizedRect();
}
@ -516,16 +515,6 @@ Page* PixmapRequest::page() const
return d->mPage;
}
void PixmapRequest::setTile( bool tile )
{
d->mTile = tile;
}
bool PixmapRequest::isTile() const
{
return d->mTile;
}
void PixmapRequest::setNormalizedRect( const NormalizedRect &rect )
{
if ( d->mNormalizedRect == rect )
@ -539,11 +528,6 @@ const NormalizedRect& PixmapRequest::normalizedRect() const
return d->mNormalizedRect;
}
Okular::TilesManager* PixmapRequestPrivate::tilesManager() const
{
return mPage->d->tilesManager(mObserver);
}
void PixmapRequestPrivate::swap()
{
qSwap( mWidth, mHeight );

View file

@ -202,8 +202,7 @@ class OKULAR_EXPORT Generator : public QObject
ReadRawData, ///< Whether the Generator can read a document directly from its raw data.
FontInfo, ///< Whether the Generator can provide information about the fonts used in the document
PageSizes, ///< Whether the Generator can change the size of the document pages.
PrintNative, ///< Whether the Generator supports native cross-platform printing (QPainter-based).
TiledRendering ///< Whether the Generator can render tiles @since 0.16 (KDE 4.10)
PrintNative ///< Whether the Generator supports native cross-platform printing (QPainter-based).
};
/**
@ -637,22 +636,6 @@ class OKULAR_EXPORT PixmapRequest
*/
Page *page() const;
/**
* Sets whether the generator should render only the given normalized
* rect or the entire page
*
* @since 0.16 (KDE 4.10)
*/
void setTile( bool tile );
/**
* Returns whether the generator should render just the region given by
* normalizedRect() or the entire page.
*
* @since 0.16 (KDE 4.10)
*/
bool isTile() const;
/**
* Sets the region of the page to request.
*

View file

@ -30,7 +30,6 @@ class PixmapGenerationThread;
class PixmapRequest;
class TextPage;
class TextPageGenerationThread;
class TilesManager;
class GeneratorPrivate
{
@ -73,7 +72,6 @@ class PixmapRequestPrivate
{
public:
void swap();
TilesManager *tilesManager() const;
DocumentObserver *mObserver;
int mPageNumber;
@ -81,8 +79,7 @@ class PixmapRequestPrivate
int mHeight;
int mPriority;
int mFeatures;
bool mForce : 1;
bool mTile : 1;
bool mForce;
Page *mPage;
NormalizedRect mNormalizedRect;
};

View file

@ -37,8 +37,6 @@
#include "rotationjob_p.h"
#include "textpage.h"
#include "textpage_p.h"
#include "tile.h"
#include "tilesmanager_p.h"
#include "utils_p.h"
#include <limits>
@ -92,15 +90,6 @@ PagePrivate::~PagePrivate()
void PagePrivate::imageRotationDone( RotationJob * job )
{
TilesManager *tm = tilesManager( job->observer() );
if ( tm )
{
QPixmap *pixmap = new QPixmap( QPixmap::fromImage( job->image() ) );
tm->setPixmap( pixmap, job->rect() );
delete pixmap;
return;
}
QMap< DocumentObserver*, PixmapObject >::iterator it = m_pixmaps.find( job->observer() );
if ( it != m_pixmaps.end() )
{
@ -201,18 +190,6 @@ void Page::setBoundingBox( const NormalizedRect& bbox )
bool Page::hasPixmap( DocumentObserver *observer, int width, int height, const NormalizedRect &rect ) const
{
TilesManager *tm = d->tilesManager( observer );
if ( tm )
{
if ( width != tm->width() || height != tm->height() )
{
tm->setSize( width, height );
return false;
}
return tm->hasPixmap( rect );
}
QMap< DocumentObserver*, PagePrivate::PixmapObject >::const_iterator it = d->m_pixmaps.constFind( observer );
if ( it == d->m_pixmaps.constEnd() )
return false;
@ -376,18 +353,6 @@ void PagePrivate::rotateAt( Rotation orientation )
m_doc->m_pageController->addRotationJob(job);
}
/**
* Rotate tiles manager
*/
QMapIterator<const DocumentObserver *, TilesManager *> i(m_tilesManagers);
while (i.hasNext()) {
i.next();
TilesManager *tm = i.value();
if ( tm )
tm->setRotation( m_rotation );
}
/**
* Rotate the object rects on the page.
*/
@ -507,14 +472,6 @@ QList< FormField * > Page::formFields() const
void Page::setPixmap( DocumentObserver *observer, QPixmap *pixmap, const NormalizedRect &rect )
{
if ( d->m_rotation == Rotation0 ) {
TilesManager *tm = d->tilesManager( observer );
if ( tm )
{
tm->setPixmap( pixmap, rect );
delete pixmap;
return;
}
QMap< DocumentObserver*, PagePrivate::PixmapObject >::iterator it = d->m_pixmaps.find( observer );
if ( it != d->m_pixmaps.end() )
{
@ -529,7 +486,7 @@ void Page::setPixmap( DocumentObserver *observer, QPixmap *pixmap, const Normali
} else {
RotationJob *job = new RotationJob( pixmap->toImage(), Rotation0, d->m_rotation, observer );
job->setPage( d );
job->setRect( TilesManager::toRotatedRect( rect, d->m_rotation ) );
job->setRect( NormalizedRect::toRotatedRect( rect, d->m_rotation ) );
d->m_doc->m_pageController->addRotationJob(job);
delete pixmap;
@ -711,17 +668,8 @@ void Page::setFormFields( const QList< FormField * >& fields )
void Page::deletePixmap( DocumentObserver *observer )
{
TilesManager *tm = d->tilesManager( observer );
if ( tm )
{
delete tm;
d->m_tilesManagers.remove(observer);
}
else
{
PagePrivate::PixmapObject object = d->m_pixmaps.take( observer );
delete object.m_pixmap;
}
PagePrivate::PixmapObject object = d->m_pixmaps.take( observer );
delete object.m_pixmap;
}
void Page::deletePixmaps()
@ -733,9 +681,6 @@ void Page::deletePixmaps()
}
d->m_pixmaps.clear();
qDeleteAll(d->m_tilesManagers);
d->m_tilesManagers.clear();
}
void Page::deleteRects()
@ -989,30 +934,3 @@ const QPixmap * Page::_o_nearestPixmap( DocumentObserver *observer, int w, int h
return pixmap;
}
bool Page::hasTilesManager( const DocumentObserver *observer ) const
{
return d->tilesManager( observer ) != 0;
}
QList<Tile> Page::tilesAt( const DocumentObserver *observer, const NormalizedRect &rect ) const
{
TilesManager *tm = d->m_tilesManagers.value( observer );
if ( tm )
return tm->tilesAt( rect, TilesManager::PixmapTile );
else
return QList<Tile>();
}
TilesManager *PagePrivate::tilesManager( const DocumentObserver *observer ) const
{
return m_tilesManagers.value( observer );
}
void PagePrivate::setTilesManager( const DocumentObserver *observer, TilesManager *tm )
{
TilesManager *old = m_tilesManagers.value( observer );
delete old;
m_tilesManagers.insert(observer, tm);
}

View file

@ -32,7 +32,6 @@ class PagePrivate;
class PageTransition;
class SourceReference;
class TextSelection;
class Tile;
/**
* @short Collector for all the data belonging to a page.
@ -368,23 +367,6 @@ class OKULAR_EXPORT Page
*/
void deleteAnnotations();
/**
* Returns whether pixmaps for the tiled observer are handled by a
* tile manager.
*
* @since 0.19 (KDE 4.13)
*/
bool hasTilesManager( const DocumentObserver *observer ) const;
/**
* Returns a list of all tiles intersecting with @p rect.
*
* The list contains only tiles with a pixmap
*
* @since 0.19 (KDE 4.13)
*/
QList<Tile> tilesAt( const DocumentObserver *observer, const NormalizedRect &rect ) const;
private:
PagePrivate* const d;
/// @cond PRIVATE

View file

@ -37,7 +37,6 @@ class PageSize;
class PageTransition;
class RotationJob;
class TextPage;
class TilesManager;
enum PageItem
{
@ -104,16 +103,6 @@ class PagePrivate
*/
void deleteTextSelections();
/**
* Get the tiles manager for the tiled @observer
*/
TilesManager *tilesManager( const DocumentObserver *observer ) const;
/**
* Set the tiles manager for the tiled @observer
*/
void setTilesManager( const DocumentObserver *observer, TilesManager *tm );
class PixmapObject
{
public:
@ -121,7 +110,6 @@ class PagePrivate
Rotation m_rotation;
};
QMap< DocumentObserver*, PixmapObject > m_pixmaps;
QMap< const DocumentObserver*, TilesManager *> m_tilesManagers;
Page *m_page;
int m_number;

View file

@ -1,56 +0,0 @@
/***************************************************************************
* Copyright (C) 2012 by Fabio D'Urso <fabiodurso@hotmail.it> *
* 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 *
* (at your option) any later version. *
***************************************************************************/
#ifndef _OKULAR_TILE_H_
#define _OKULAR_TILE_H_
#include "area.h"
#include <QPixmap>
namespace Okular {
/**
* This class represents a rectangular portion of a page.
*
* It doesn't take ownership of pixmap
*
* @since 0.16 (KDE 4.10)
*/
class OKULAR_EXPORT Tile
{
public:
Tile( const NormalizedRect &rect, QPixmap *pixmap, bool isValid );
Tile( const Tile &t );
~Tile();
/**
* Location of the tile
*/
NormalizedRect rect() const;
/**
* Pixmap (may also be NULL)
*/
QPixmap * pixmap() const;
/**
* True if the pixmap is available and updated
*/
bool isValid() const;
Tile& operator=( const Tile &other );
private:
class Private;
Private * d;
};
}
#endif // _OKULAR_TILE_H_

View file

@ -1,709 +0,0 @@
/***************************************************************************
* Copyright (C) 2012 by Mailson Menezes <mailson@gmail.com> *
* 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 *
* (at your option) any later version. *
***************************************************************************/
#include "tilesmanager_p.h"
#include <QPixmap>
#include <QtCore/qmath.h>
#include <QList>
#include <QPainter>
#include "tile.h"
#define TILES_MAXSIZE 2000000
using namespace Okular;
static bool rankedTilesLessThan( TileNode *t1, TileNode *t2 )
{
// Order tiles by its dirty state and then by distance from the viewport.
if ( t1->dirty == t2->dirty )
return t1->distance < t2->distance;
return !t1->dirty;
}
class TilesManager::Private
{
public:
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 );
/**
* Mark @p tile and all its children as dirty
*/
static void markDirty( TileNode &tile );
/**
* Deletes all tiles, recursively
*/
void deleteTiles( const TileNode &tile );
void markParentDirty( const TileNode &tile );
void rankTiles( TileNode &tile, QList<TileNode*> &rankedTiles, const NormalizedRect &visibleRect, int visiblePageNumber );
/**
* Since the tile can be large enough to occupy a significant amount of
* space, they may be split in more tiles. This operation is performed
* when the tiles of a certain region is requested and they are bigger
* than an arbitrary value. Only tiles intersecting the desired region
* are split. There's no need to do this for the entire page.
*/
void split( TileNode &tile, const NormalizedRect &rect );
/**
* Checks whether the tile's size is bigger than an arbitrary value and
* performs the split operation returning true.
* Otherwise it just returns false, without performing any operation.
*/
bool splitBigTiles( TileNode &tile, const NormalizedRect &rect );
// The page is split in a 4x4 grid of tiles
TileNode tiles[16];
int width;
int height;
int pageNumber;
qulonglong totalPixels;
Rotation rotation;
NormalizedRect visibleRect;
NormalizedRect requestRect;
int requestWidth;
int requestHeight;
};
TilesManager::Private::Private()
: width( 0 )
, height( 0 )
, pageNumber( 0 )
, totalPixels( 0 )
, rotation( Rotation0 )
, requestRect( NormalizedRect() )
, requestWidth( 0 )
, requestHeight( 0 )
{
}
TilesManager::TilesManager( int pageNumber, int width, int height, Rotation rotation )
: d( new Private )
{
d->pageNumber = pageNumber;
d->width = width;
d->height = height;
d->rotation = rotation;
// The page is split in a 4x4 grid of tiles
const double dim = 0.25;
for ( int i = 0; i < 16; ++i )
{
int x = i % 4;
int y = i / 4;
d->tiles[ i ].rect = NormalizedRect( x*dim, y*dim, x*dim+dim, y*dim+dim );
}
}
TilesManager::~TilesManager()
{
for ( int i = 0; i < 16; ++i )
d->deleteTiles( d->tiles[ i ] );
delete d;
}
void TilesManager::Private::deleteTiles( const TileNode &tile )
{
if ( tile.pixmap )
{
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
delete tile.pixmap;
}
if ( tile.nTiles > 0 )
{
for ( int i = 0; i < tile.nTiles; ++i )
deleteTiles( tile.tiles[ i ] );
delete [] tile.tiles;
}
}
void TilesManager::setSize( int width, int height )
{
if ( width == d->width && height == d->height )
return;
d->width = width;
d->height = height;
markDirty();
}
int TilesManager::width() const
{
return d->width;
}
int TilesManager::height() const
{
return d->height;
}
void TilesManager::setRotation( Rotation rotation )
{
if ( rotation == d->rotation )
return;
d->rotation = rotation;
}
Rotation TilesManager::rotation() const
{
return d->rotation;
}
void TilesManager::markDirty()
{
for ( int i = 0; i < 16; ++i )
{
TilesManager::Private::markDirty( d->tiles[ i ] );
}
}
void TilesManager::Private::markDirty( TileNode &tile )
{
tile.dirty = true;
for ( int i = 0; i < tile.nTiles; ++i )
{
markDirty( tile.tiles[ i ] );
}
}
void TilesManager::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect )
{
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 )
{
qSwap(w, h);
pixmapSize.transpose();
}
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 ] );
}
}
void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile )
{
QRect pixmapRect = TilesManager::toRotatedRect( rect, rotation ).geometry( width, height );
// Exclude tiles outside the viewport
if ( !tile.rect.intersects( rect ) )
return;
// if the tile is not entirely within the viewport (the tile intersects an
// edged of the viewport), attempt to set the pixmap in the children tiles
if ( !((tile.rect & rect) == tile.rect) )
{
// paint children tiles
if ( tile.nTiles > 0 )
{
for ( int i = 0; i < tile.nTiles; ++i )
setPixmap( pixmap, rect, tile.tiles[ i ] );
delete tile.pixmap;
tile.pixmap = 0;
}
return;
}
// the tile lies entirely within the viewport
if ( tile.nTiles == 0 )
{
tile.dirty = false;
// check whether the tile size is big and split it if necessary
if ( !splitBigTiles( tile, rect ) )
{
if ( tile.pixmap )
{
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();
}
else
{
if ( tile.pixmap )
{
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
delete tile.pixmap;
tile.pixmap = 0;
}
for ( int i = 0; i < tile.nTiles; ++i )
setPixmap( pixmap, rect, tile.tiles[ i ] );
}
}
else
{
QRect tileRect = tile.rect.geometry( width, height );
// sets the pixmap of the children tiles. if the tile's size is too
// small, discards the children tiles and use the current one
if ( tileRect.width()*tileRect.height() >= TILES_MAXSIZE )
{
tile.dirty = false;
if ( tile.pixmap )
{
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
delete tile.pixmap;
tile.pixmap = 0;
}
for ( int i = 0; i < tile.nTiles; ++i )
setPixmap( pixmap, rect, tile.tiles[ i ] );
}
else
{
// remove children tiles
for ( int i = 0; i < tile.nTiles; ++i )
{
deleteTiles( tile.tiles[ i ] );
tile.tiles[ i ].pixmap = 0;
}
delete [] tile.tiles;
tile.tiles = 0;
tile.nTiles = 0;
// paint tile
if ( tile.pixmap )
{
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;
}
}
}
bool TilesManager::hasPixmap( const NormalizedRect &rect )
{
NormalizedRect rotatedRect = fromRotatedRect( rect, d->rotation );
for ( int i = 0; i < 16; ++i )
{
if ( !d->hasPixmap( rotatedRect, d->tiles[ i ] ) )
return false;
}
return true;
}
bool TilesManager::Private::hasPixmap( const NormalizedRect &rect, const TileNode &tile ) const
{
if ( !tile.rect.intersects( rect ) )
return true;
if ( tile.nTiles == 0 )
return tile.isValid();
// all children tiles are clean. doesn't need to go deeper
if ( !tile.dirty )
return true;
for ( int i = 0; i < tile.nTiles; ++i )
{
if ( !hasPixmap( rect, tile.tiles[ i ] ) )
return false;
}
return true;
}
QList<Tile> TilesManager::tilesAt( const NormalizedRect &rect, TileLeaf tileLeaf )
{
QList<Tile> result;
NormalizedRect rotatedRect = fromRotatedRect( rect, d->rotation );
for ( int i = 0; i < 16; ++i )
{
d->tilesAt( rotatedRect, d->tiles[ i ], result, tileLeaf );
}
return result;
}
void TilesManager::Private::tilesAt( const NormalizedRect &rect, TileNode &tile, QList<Tile> &result, TileLeaf tileLeaf )
{
if ( !tile.rect.intersects( rect ) )
return;
// split big tiles before the requests are made, otherwise we would end up
// requesting huge areas unnecessarily
splitBigTiles( tile, rect );
if ( ( tileLeaf == TerminalTile && tile.nTiles == 0 ) || ( tileLeaf == PixmapTile && tile.pixmap ) )
{
NormalizedRect rotatedRect;
if ( rotation != Rotation0 )
rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation );
else
rotatedRect = tile.rect;
if ( tile.pixmap && tileLeaf == PixmapTile && tile.rotation != rotation )
{
// Lazy tiles rotation
int angleToRotate = (rotation - tile.rotation)*90;
int xOffset = 0, yOffset = 0;
int w = 0, h = 0;
switch( angleToRotate )
{
case 0:
xOffset = 0;
yOffset = 0;
w = tile.pixmap->width();
h = tile.pixmap->height();
break;
case 90:
case -270:
xOffset = 0;
yOffset = -tile.pixmap->height();
w = tile.pixmap->height();
h = tile.pixmap->width();
break;
case 180:
case -180:
xOffset = -tile.pixmap->width();
yOffset = -tile.pixmap->height();
w = tile.pixmap->width();
h = tile.pixmap->height();
break;
case 270:
case -90:
xOffset = -tile.pixmap->width();
yOffset = 0;
w = tile.pixmap->height();
h = tile.pixmap->width();
break;
}
QPixmap *rotatedPixmap = new QPixmap( w, h );
QPainter p( rotatedPixmap );
p.rotate( angleToRotate );
p.translate( xOffset, yOffset );
p.drawPixmap( 0, 0, *tile.pixmap );
p.end();
delete tile.pixmap;
tile.pixmap = rotatedPixmap;
tile.rotation = rotation;
}
result.append( Tile( rotatedRect, tile.pixmap, tile.isValid() ) );
}
else
{
for ( int i = 0; i < tile.nTiles; ++i )
tilesAt( rect, tile.tiles[ i ], result, tileLeaf );
}
}
qulonglong TilesManager::totalMemory() const
{
return 4*d->totalPixels;
}
void TilesManager::cleanupPixmapMemory( qulonglong numberOfBytes, const NormalizedRect &visibleRect, int visiblePageNumber )
{
QList<TileNode*> rankedTiles;
for ( int i = 0; i < 16; ++i )
{
d->rankTiles( d->tiles[ i ], rankedTiles, visibleRect, visiblePageNumber );
}
qSort( rankedTiles.begin(), rankedTiles.end(), rankedTilesLessThan );
while ( numberOfBytes > 0 && !rankedTiles.isEmpty() )
{
TileNode *tile = rankedTiles.takeLast();
if ( !tile->pixmap )
continue;
// do not evict visible pixmaps
if ( tile->rect.intersects( visibleRect ) )
continue;
qulonglong pixels = tile->pixmap->width()*tile->pixmap->height();
d->totalPixels -= pixels;
if ( numberOfBytes < 4*pixels )
numberOfBytes = 0;
else
numberOfBytes -= 4*pixels;
delete tile->pixmap;
tile->pixmap = 0;
d->markParentDirty( *tile );
}
}
void TilesManager::Private::markParentDirty( const TileNode &tile )
{
if ( !tile.parent )
return;
if ( !tile.parent->dirty )
{
tile.parent->dirty = true;
markParentDirty( *tile.parent );
}
}
void TilesManager::Private::rankTiles( TileNode &tile, QList<TileNode*> &rankedTiles, const NormalizedRect &visibleRect, int visiblePageNumber )
{
// If the page is visible, visibleRect is not null.
// Otherwise we use the number of one of the visible pages to calculate the
// distance.
// Note that the current page may be visible and yet its pageNumber is
// different from visiblePageNumber. Since we only use this value on hidden
// pages, any visible page number will fit.
if ( visibleRect.isNull() && visiblePageNumber < 0 )
return;
if ( tile.pixmap )
{
// Update distance
if ( !visibleRect.isNull() )
{
NormalizedPoint viewportCenter = visibleRect.center();
NormalizedPoint tileCenter = tile.rect.center();
// Manhattan distance. It's a good and fast approximation.
tile.distance = qAbs(viewportCenter.x - tileCenter.x) + qAbs(viewportCenter.y - tileCenter.y);
}
else
{
// For non visible pages only the vertical distance is used
if ( pageNumber < visiblePageNumber )
tile.distance = 1 - tile.rect.bottom;
else
tile.distance = tile.rect.top;
}
rankedTiles.append( &tile );
}
else
{
for ( int i = 0; i < tile.nTiles; ++i )
{
rankTiles( tile.tiles[ i ], rankedTiles, visibleRect, visiblePageNumber );
}
}
}
bool TilesManager::isRequesting( const NormalizedRect &rect, int pageWidth, int pageHeight ) const
{
return rect == d->requestRect && pageWidth == d->requestWidth && pageHeight == d->requestHeight;
}
void TilesManager::setRequest( const NormalizedRect &rect, int pageWidth, int pageHeight )
{
d->requestRect = rect;
d->requestWidth = pageWidth;
d->requestHeight = pageHeight;
}
bool TilesManager::Private::splitBigTiles( TileNode &tile, const NormalizedRect &rect )
{
QRect tileRect = tile.rect.geometry( width, height );
if ( tileRect.width()*tileRect.height() < TILES_MAXSIZE )
return false;
split( tile, rect );
return true;
}
void TilesManager::Private::split( TileNode &tile, const NormalizedRect &rect )
{
if ( tile.nTiles != 0 )
return;
if ( rect.isNull() || !tile.rect.intersects( rect ) )
return;
tile.nTiles = 4;
tile.tiles = new TileNode[4];
double hCenter = (tile.rect.left + tile.rect.right)/2;
double vCenter = (tile.rect.top + tile.rect.bottom)/2;
tile.tiles[0].rect = NormalizedRect( tile.rect.left, tile.rect.top, hCenter, vCenter );
tile.tiles[1].rect = NormalizedRect( hCenter, tile.rect.top, tile.rect.right, vCenter );
tile.tiles[2].rect = NormalizedRect( tile.rect.left, vCenter, hCenter, tile.rect.bottom );
tile.tiles[3].rect = NormalizedRect( hCenter, vCenter, tile.rect.right, tile.rect.bottom );
for ( int i = 0; i < tile.nTiles; ++i )
{
tile.tiles[ i ].parent = &tile;
splitBigTiles( tile.tiles[ i ], rect );
}
}
NormalizedRect TilesManager::fromRotatedRect( const NormalizedRect &rect, Rotation rotation )
{
if ( rotation == Rotation0 )
return rect;
NormalizedRect newRect;
switch ( rotation )
{
case Rotation90:
newRect = NormalizedRect( rect.top, 1 - rect.right, rect.bottom, 1 - rect.left );
break;
case Rotation180:
newRect = NormalizedRect( 1 - rect.right, 1 - rect.bottom, 1 - rect.left, 1 - rect.top );
break;
case Rotation270:
newRect = NormalizedRect( 1 - rect.bottom, rect.left, 1 - rect.top, rect.right );
break;
default:
newRect = rect;
break;
}
return newRect;
}
NormalizedRect TilesManager::toRotatedRect( const NormalizedRect &rect, Rotation rotation )
{
if ( rotation == Rotation0 )
return rect;
NormalizedRect newRect;
switch ( rotation )
{
case Rotation90:
newRect = NormalizedRect( 1 - rect.bottom, rect.left, 1 - rect.top, rect.right );
break;
case Rotation180:
newRect = NormalizedRect( 1 - rect.right, 1 - rect.bottom, 1 - rect.left, 1 - rect.top );
break;
case Rotation270:
newRect = NormalizedRect( rect.top, 1 - rect.right, rect.bottom, 1 - rect.left );
break;
default:
newRect = rect;
break;
}
return newRect;
}
TileNode::TileNode()
: pixmap( 0 )
, rotation( Rotation0 )
, dirty ( true )
, distance( -1 )
, tiles( 0 )
, nTiles( 0 )
, parent( 0 )
{
}
bool TileNode::isValid() const
{
return pixmap && !dirty;
}
class Tile::Private
{
public:
Private();
NormalizedRect rect;
QPixmap *pixmap;
bool isValid;
};
Tile::Private::Private()
: pixmap( 0 )
, isValid( false )
{
}
Tile::Tile( const NormalizedRect &rect, QPixmap *pixmap, bool isValid )
: d( new Tile::Private )
{
d->rect = rect;
d->pixmap = pixmap;
d->isValid = isValid;
}
Tile::Tile( const Tile &t )
: d( new Tile::Private )
{
d->rect = t.d->rect;
d->pixmap = t.d->pixmap;
d->isValid = t.d->isValid;
}
Tile& Tile::operator=( const Tile &other )
{
if ( this == &other )
return *this;
d->rect = other.d->rect;
d->pixmap = other.d->pixmap;
d->isValid = other.d->isValid;
return *this;
}
Tile::~Tile()
{
delete d;
}
NormalizedRect Tile::rect() const
{
return d->rect;
}
QPixmap * Tile::pixmap() const
{
return d->pixmap;
}
bool Tile::isValid() const
{
return d->isValid;
}

View file

@ -1,207 +0,0 @@
/***************************************************************************
* Copyright (C) 2012 by Mailson Menezes <mailson@gmail.com> *
* 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 *
* (at your option) any later version. *
***************************************************************************/
#ifndef _OKULAR_TILES_MANAGER_P_H_
#define _OKULAR_TILES_MANAGER_P_H_
#include "okular_export.h"
#include "area.h"
#include <QPixmap>
namespace Okular {
class Tile;
/**
* Node in the quadtree structure used by the tiles manager to store tiles.
*
* Except for the first level, the tiles manager stores tiles in a quadtree
* structure.
* Each node stores the pixmap of a tile and its location on the page.
* There's a limit on the size of the pixmaps (TILES_MAXSIZE, defined in
* tilesmanager.cpp), and tiles that are bigger than that value are split into
* four children tiles, which are stored as children of the original tile.
* If children tiles are still too big, they are recursively split again.
* If the zoom level changes and a big tile goes below the limit, it is merged
* back into a leaf tile.
*/
class TileNode
{
public:
TileNode();
bool isValid() const;
/**
* Location on the page in normalized coords
*/
NormalizedRect rect;
/**
* Associated pixmap or NULL if not present
*
* For each node, it is guaranteed that there's no more than one pixmap
* along the path from the root to the node itself.
* In fact, it is very frequent that a leaf node has no pixmap and one
* of its ancestors has. Such a situation shows, for example, when the
* parent tile still has a dirty tile from a previous lower zoom level.
*/
QPixmap *pixmap;
/**
* Rotation of this individual tile.
*
* A rotation to the page does not immediately rotates the pixmaps in
* cache. This operation happens when pixmaps are going to be used.
*/
Rotation rotation;
/**
* Whether the tile needs to be repainted (after a zoom or rotation)
* If a tile doesn't have a pixmap but all its children are updated
* (dirty = false), the parent tile is also considered updated.
*/
bool dirty;
/**
* Distance between the tile and the viewport.
* This is used by the evicting algorithm.
*/
double distance;
/**
* Children tiles
* When a tile is split into multiple tiles, they're added as children.
* nTiles can be either 0 (in leaf tiles) or 4 (in split tiles).
*/
TileNode *tiles;
int nTiles;
TileNode *parent;
};
/**
* @short Tiles management
*
* This class has direct access to all tiles and handles how they should be
* stored, deleted and retrieved. Each tiles manager only handles one page.
*
* The tiles manager is a tree of tiles. At first the page is divided in a 4x4
* grid of 16 tiles. Then each of these tiles can be recursively split in 4
* subtiles so that we keep the size of each pixmap inside a safe interval.
*/
class TilesManager
{
public:
enum TileLeaf
{
TerminalTile, ///< Return tiles without children
PixmapTile ///< Return only tiles with pixmap
};
TilesManager( int pageNumber, int width, int height, Rotation rotation = Rotation0 );
~TilesManager();
/**
* Sets the pixmap of the tiles covered by @p rect (which represents
* the location of @p pixmap on the page).
* @p pixmap may cover an area which contains multiple tiles. So each
* tile we get a cropped part of the @p pixmap.
*
* 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 );
/**
* Checks whether all tiles intersecting with @p rect are available.
* Returns false if at least one tile needs to be repainted (the tile
* is dirty).
*/
bool hasPixmap( const NormalizedRect &rect );
/**
* Returns a list of all tiles intersecting with @p rect.
*
* As to avoid requests of big areas, each traversed tile is checked
* for its size and split if necessary.
*
* @param tileLeaf Indicate the type of tile to return
*/
QList<Tile> tilesAt( const NormalizedRect &rect, TileLeaf tileLeaf );
/**
* The total memory consumed by the tiles manager
*/
qulonglong totalMemory() const;
/**
* Removes at least @p numberOfBytes bytes worth of tiles (least ranked
* tiles are removed first).
* Set @p visibleRect to the visible region of the page. Set a
* @p visiblePageNumber if the current page is not visible.
* Visible tiles are not discarded.
*/
void cleanupPixmapMemory( qulonglong numberOfBytes, const NormalizedRect &visibleRect, int visiblePageNumber );
/**
* Checks whether a given region has already been requested
*/
bool isRequesting( const NormalizedRect &rect, int pageWidth, int pageHeight ) const;
/**
* Sets a region to be requested so the tiles manager knows which
* pixmaps to expect and discard those not useful anymore (late pixmaps)
*/
void setRequest( const NormalizedRect &rect, int pageWidth, int pageHeight );
/**
* Inform the new size of the page and mark all tiles to repaint
*/
void setSize( int width, int height );
/**
* Gets the width of the page in tiles manager
*/
int width() const;
/**
* Gets the height of the page in tiles manager
*/
int height() const;
/**
* Inform the new rotation of the page
*/
void setRotation( Rotation rotation );
Rotation rotation() const;
/**
* Mark all tiles as dirty
*/
void markDirty();
/**
* Returns a rotated NormalizedRect given a @p rotation
*/
static NormalizedRect toRotatedRect( const NormalizedRect &rect, Rotation rotation );
/**
* Returns a non rotated version of @p rect, which is rotated by @p rotation
*/
static NormalizedRect fromRotatedRect( const NormalizedRect &rect, Rotation rotation );
private:
class Private;
Private * const d;
friend class Private;
};
}
#endif // _OKULAR_TILES_MANAGER_P_H_

View file

@ -128,10 +128,6 @@ void MagnifierView::requestPixmap()
Okular::PixmapRequest *p = new Okular::PixmapRequest( this, m_current, full_width, full_height, PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous );
if ( m_page->hasTilesManager( this ) ) {
p->setTile( true );
}
// request a little bit bigger rectangle then currently viewed, but not the full scale page
const double rect_width = (nrect.right - nrect.left) * 0.5,
rect_height = (nrect.bottom - nrect.top) * 0.5;

View file

@ -32,7 +32,6 @@
#include "guiutils.h"
#include "settings.h"
#include "core/observer.h"
#include "core/tile.h"
#include "settings_core.h"
#include "core/document_p.h"
@ -89,33 +88,27 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
}
destPainter->fillRect( limits, backgroundColor );
const bool hasTilesManager = page->hasTilesManager( observer );
const QPixmap *pixmap = 0;
/** 1 - RETRIEVE THE 'PAGE+ID' PIXMAP OR A SIMILAR 'PAGE' ONE **/
const QPixmap *pixmap = page->_o_nearestPixmap( observer, scaledWidth, scaledHeight );
if ( !hasTilesManager )
/** 1B - IF NO PIXMAP, DRAW EMPTY PAGE **/
double pixmapRescaleRatio = pixmap ? scaledWidth / (double)pixmap->width() : -1;
long pixmapPixels = pixmap ? (long)pixmap->width() * (long)pixmap->height() : 0;
if ( !pixmap || pixmapRescaleRatio > 20.0 || pixmapRescaleRatio < 0.25 ||
(scaledWidth != pixmap->width() && pixmapPixels > 6000000L) )
{
/** 1 - RETRIEVE THE 'PAGE+ID' PIXMAP OR A SIMILAR 'PAGE' ONE **/
pixmap = page->_o_nearestPixmap( observer, scaledWidth, scaledHeight );
/** 1B - IF NO PIXMAP, DRAW EMPTY PAGE **/
double pixmapRescaleRatio = pixmap ? scaledWidth / (double)pixmap->width() : -1;
long pixmapPixels = pixmap ? (long)pixmap->width() * (long)pixmap->height() : 0;
if ( !pixmap || pixmapRescaleRatio > 20.0 || pixmapRescaleRatio < 0.25 ||
(scaledWidth != pixmap->width() && pixmapPixels > 6000000L) )
// draw something on the blank page: the okular icon or a cross (as a fallback)
if ( !busyPixmap->isNull() )
{
// draw something on the blank page: the okular icon or a cross (as a fallback)
if ( !busyPixmap->isNull() )
{
destPainter->drawPixmap( QPoint( 10, 10 ), *busyPixmap );
}
else
{
destPainter->setPen( Qt::gray );
destPainter->drawLine( 0, 0, croppedWidth-1, croppedHeight-1 );
destPainter->drawLine( 0, croppedHeight-1, croppedWidth-1, 0 );
}
return;
destPainter->drawPixmap( QPoint( 10, 10 ), *busyPixmap );
}
else
{
destPainter->setPen( Qt::gray );
destPainter->drawLine( 0, 0, croppedWidth-1, croppedHeight-1 );
destPainter->drawLine( 0, croppedHeight-1, croppedWidth-1, 0 );
}
return;
}
/** 2 - FIND OUT WHAT TO PAINT (Flags + Configuration + Presence) **/
@ -243,41 +236,17 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
/** 4A -- REGULAR FLOW. PAINT PIXMAP NORMAL OR RESCALED USING GIVEN QPAINTER **/
if ( !useBackBuffer )
{
if ( hasTilesManager )
{
const Okular::NormalizedRect normalizedLimits( limitsInPixmap, scaledWidth, scaledHeight );
const QList<Okular::Tile> tiles = page->tilesAt( observer, normalizedLimits );
QList<Okular::Tile>::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd();
while ( tIt != tEnd )
{
const Okular::Tile &tile = *tIt;
QRect tileRect = tile.rect().geometry( scaledWidth, scaledHeight ).translated( -scaledCrop.topLeft() );
QRect limitsInTile = limits & tileRect;
if ( !limitsInTile.isEmpty() )
{
if ( tile.pixmap()->width() == tileRect.width() && tile.pixmap()->height() == tileRect.height() )
destPainter->drawPixmap( limitsInTile.topLeft(), *(tile.pixmap()),
limitsInTile.translated( -tileRect.topLeft() ) );
else
destPainter->drawPixmap( tileRect, *(tile.pixmap()) );
}
tIt++;
}
}
// 4A.1. if size is ok, draw the page pixmap using painter
if ( pixmap->width() == scaledWidth && pixmap->height() == scaledHeight )
destPainter->drawPixmap( limits.topLeft(), *pixmap, limitsInPixmap );
// else draw a scaled portion of the magnified pixmap
else
{
// 4A.1. if size is ok, draw the page pixmap using painter
if ( pixmap->width() == scaledWidth && pixmap->height() == scaledHeight )
destPainter->drawPixmap( limits.topLeft(), *pixmap, limitsInPixmap );
// else draw a scaled portion of the magnified pixmap
else
{
QImage destImage;
scalePixmapOnImage( destImage, pixmap, scaledWidth, scaledHeight, limitsInPixmap );
destPainter->drawImage( limits.left(), limits.top(), destImage, 0, 0,
limits.width(),limits.height() );
}
QImage destImage;
scalePixmapOnImage( destImage, pixmap, scaledWidth, scaledHeight, limitsInPixmap );
destPainter->drawImage( limits.left(), limits.top(), destImage, 0, 0,
limits.width(),limits.height() );
}
// 4A.2. active painter is the one passed to this method
@ -295,50 +264,11 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
else
has_alpha = true;
if ( hasTilesManager )
{
backImage = QImage( limits.width(), limits.height(), QImage::Format_ARGB32_Premultiplied );
backImage.fill( paperColor.rgb() );
QPainter p( &backImage );
const Okular::NormalizedRect normalizedLimits( limitsInPixmap, scaledWidth, scaledHeight );
const QList<Okular::Tile> tiles = page->tilesAt( observer, normalizedLimits );
QList<Okular::Tile>::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd();
while ( tIt != tEnd )
{
const Okular::Tile &tile = *tIt;
QRect tileRect = tile.rect().geometry( scaledWidth, scaledHeight ).translated( -scaledCrop.topLeft() );
QRect limitsInTile = limits & tileRect;
if ( !limitsInTile.isEmpty() )
{
if ( !tile.pixmap()->hasAlpha() )
has_alpha = false;
if ( tile.pixmap()->width() == tileRect.width() && tile.pixmap()->height() == tileRect.height() )
{
p.drawPixmap( limitsInTile.translated( -limits.topLeft() ).topLeft(), *(tile.pixmap()),
limitsInTile.translated( -tileRect.topLeft() ) );
}
else
{
double xScale = tile.pixmap()->width() / (double)tileRect.width();
double yScale = tile.pixmap()->height() / (double)tileRect.height();
QTransform transform( xScale, 0, 0, yScale, 0, 0 );
p.drawPixmap( limitsInTile.translated( -limits.topLeft() ), *(tile.pixmap()),
transform.mapRect( limitsInTile ).translated( -transform.mapRect( tileRect ).topLeft() ) );
}
}
++tIt;
}
p.end();
}
// 4B.1. draw the page pixmap: normal or scaled
if ( pixmap->width() == scaledWidth && pixmap->height() == scaledHeight )
cropPixmapOnImage( backImage, pixmap, limitsInPixmap );
else
{
// 4B.1. draw the page pixmap: normal or scaled
if ( pixmap->width() == scaledWidth && pixmap->height() == scaledHeight )
cropPixmapOnImage( backImage, pixmap, limitsInPixmap );
else
scalePixmapOnImage( backImage, pixmap, scaledWidth, scaledHeight, limitsInPixmap );
}
scalePixmapOnImage( backImage, pixmap, scaledWidth, scaledHeight, limitsInPixmap );
// 4B.2. modify pixmap following accessibility settings
if ( bufferAccessibility )

View file

@ -80,7 +80,6 @@
#include "core/movie.h"
#include "core/audioplayer.h"
#include "core/sourcereference.h"
#include "core/tile.h"
#include "settings.h"
#include "settings_core.h"
#include "magnifierview.h"
@ -1059,8 +1058,11 @@ void PageView::updateActionState( bool haspages, bool documentChanged, bool hasf
d->aSpeakDoc->setEnabled( enablettsactions );
d->aSpeakPage->setEnabled( enablettsactions );
}
if (d->aMouseMagnifier)
d->aMouseMagnifier->setEnabled(d->document->supportsTiles());
if (d->aMouseMagnifier) {
// FIXME: works only for some zoom-levels
d->aMouseMagnifier->setVisible(false);
d->aMouseMagnifier->setEnabled(false);
}
}
bool PageView::areSourceLocationsShownGraphically() const
@ -1813,7 +1815,7 @@ void PageView::mouseMoveEvent( QMouseEvent * e )
deltaY = mouseContainer.height() - absDeltaY;
}
const float upperZoomLimit = d->document->supportsTiles() ? 15.99 : 3.99;
const float upperZoomLimit = 3.99;
if ( mouseY <= mouseContainer.top() + 4 &&
d->zoomFactor < upperZoomLimit )
{
@ -2421,7 +2423,7 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
double nX = (double)(selRect.left() + selRect.right()) / (2.0 * (double)contentAreaWidth());
double nY = (double)(selRect.top() + selRect.bottom()) / (2.0 * (double)contentAreaHeight());
const float upperZoomLimit = d->document->supportsTiles() ? 16.0 : 4.0;
const float upperZoomLimit = 4.0;
if ( d->zoomFactor <= upperZoomLimit || zoom <= 1.0 )
{
d->zoomFactor *= zoom;
@ -3637,7 +3639,7 @@ void PageView::updateZoom( ZoomMode newZoomMode )
d->zoomFactor = -1;
break;
}
const float upperZoomLimit = d->document->supportsTiles() ? 16.0 : 4.0;
const float upperZoomLimit = 4.0;
if ( newFactor > upperZoomLimit )
newFactor = upperZoomLimit;
if ( newFactor < 0.1 )
@ -3689,8 +3691,6 @@ void PageView::updateZoomText()
int idx = 0, selIdx = 3;
bool inserted = false; //use: "d->zoomMode != ZoomFixed" to hide Fit/* zoom ratio
int zoomValueCount = 11;
if ( d->document->supportsTiles() )
zoomValueCount = 13;
while ( idx < zoomValueCount || !inserted )
{
float value = idx < zoomValueCount ? kZoomValues[ idx ] : newFactor;
@ -4208,21 +4208,9 @@ static void slotRequestPreloadPixmap( Okular::DocumentObserver * observer, const
{
Okular::PixmapRequest::PixmapRequestFeatures requestFeatures = Okular::PixmapRequest::Preload;
requestFeatures |= Okular::PixmapRequest::Asynchronous;
const bool pageHasTilesManager = i->page()->hasTilesManager( observer );
if ( pageHasTilesManager && !preRenderRegion.isNull() )
{
Okular::PixmapRequest * p = new Okular::PixmapRequest( observer, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO, requestFeatures );
requestedPixmaps->push_back( p );
p->setNormalizedRect( preRenderRegion );
p->setTile( true );
}
else if ( !pageHasTilesManager )
{
Okular::PixmapRequest * p = new Okular::PixmapRequest( observer, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO, requestFeatures );
requestedPixmaps->push_back( p );
p->setNormalizedRect( preRenderRegion );
}
Okular::PixmapRequest * p = new Okular::PixmapRequest( observer, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO, requestFeatures );
requestedPixmaps->push_back( p );
p->setNormalizedRect( preRenderRegion );
}
}
@ -4300,18 +4288,8 @@ void PageView::slotRequestVisiblePixmaps( int newValue )
kWarning() << "checking for text for page" << i->pageNumber() << "=" << i->page()->hasTextPage();
#endif
Okular::NormalizedRect expandedVisibleRect = vItem->rect;
if ( i->page()->hasTilesManager( this ) && Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low )
{
double rectMargin = pixelsToExpand/(double)i->uncroppedHeight();
expandedVisibleRect.left = qMax( 0.0, vItem->rect.left - rectMargin );
expandedVisibleRect.top = qMax( 0.0, vItem->rect.top - rectMargin );
expandedVisibleRect.right = qMin( 1.0, vItem->rect.right + rectMargin );
expandedVisibleRect.bottom = qMin( 1.0, vItem->rect.bottom + rectMargin );
}
// if the item has not the right pixmap, add a request for it
if ( !i->page()->hasPixmap( this, i->uncroppedWidth(), i->uncroppedHeight(), expandedVisibleRect ) )
if ( !i->page()->hasPixmap( this, i->uncroppedWidth(), i->uncroppedHeight(), vItem->rect ) )
{
#ifdef PAGEVIEW_DEBUG
kWarning() << "rerequesting visible pixmaps for page" << i->pageNumber() << "!";
@ -4319,13 +4297,7 @@ void PageView::slotRequestVisiblePixmaps( int newValue )
Okular::PixmapRequest * p = new Okular::PixmapRequest( this, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous );
requestedPixmaps.push_back( p );
if ( i->page()->hasTilesManager( this ) )
{
p->setNormalizedRect( expandedVisibleRect );
p->setTile( true );
}
else
p->setNormalizedRect( vItem->rect );
p->setNormalizedRect( vItem->rect );
}
// look for the item closest to viewport center and the relative