2012-07-11 00:48:30 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* 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. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
2012-08-17 18:23:58 +00:00
|
|
|
#include "tilesmanager_p.h"
|
2012-07-07 17:40:52 +00:00
|
|
|
|
|
|
|
#include <QPixmap>
|
2018-08-31 09:23:45 +00:00
|
|
|
#include <qmath.h>
|
2012-08-13 04:09:05 +00:00
|
|
|
#include <QList>
|
2012-11-10 17:50:58 +00:00
|
|
|
#include <QPainter>
|
2012-07-07 17:40:52 +00:00
|
|
|
|
2012-11-08 20:29:09 +00:00
|
|
|
#include "tile.h"
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
#define TILES_MAXSIZE 2000000
|
2012-08-13 04:09:05 +00:00
|
|
|
|
2012-07-07 17:40:52 +00:00
|
|
|
using namespace Okular;
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
static bool rankedTilesLessThan( TileNode *t1, TileNode *t2 )
|
2012-08-13 04:09:05 +00:00
|
|
|
{
|
2012-11-08 13:58:08 +00:00
|
|
|
// Order tiles by its dirty state and then by distance from the viewport.
|
2012-11-08 11:50:33 +00:00
|
|
|
if ( t1->dirty == t2->dirty )
|
2012-11-08 13:58:08 +00:00
|
|
|
return t1->distance < t2->distance;
|
2012-08-13 04:09:05 +00:00
|
|
|
|
|
|
|
return !t1->dirty;
|
|
|
|
}
|
|
|
|
|
2012-08-04 21:00:19 +00:00
|
|
|
class TilesManager::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private();
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
bool hasPixmap( const NormalizedRect &rect, const TileNode &tile ) const;
|
2012-11-12 14:55:13 +00:00
|
|
|
void tilesAt( const NormalizedRect &rect, TileNode &tile, QList<Tile> &result, TileLeaf tileLeaf );
|
2018-02-01 18:42:37 +00:00
|
|
|
void setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile, bool isPartialPixmap );
|
2012-08-19 21:54:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark @p tile and all its children as dirty
|
|
|
|
*/
|
2012-11-08 15:41:00 +00:00
|
|
|
static void markDirty( TileNode &tile );
|
2012-08-19 21:54:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes all tiles, recursively
|
|
|
|
*/
|
2012-11-08 15:41:00 +00:00
|
|
|
void deleteTiles( const TileNode &tile );
|
2012-08-04 21:00:19 +00:00
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
void markParentDirty( const TileNode &tile );
|
|
|
|
void rankTiles( TileNode &tile, QList<TileNode*> &rankedTiles, const NormalizedRect &visibleRect, int visiblePageNumber );
|
2012-08-19 21:54:47 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2012-11-08 15:41:00 +00:00
|
|
|
void split( TileNode &tile, const NormalizedRect &rect );
|
2012-08-13 04:09:05 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2012-11-08 15:41:00 +00:00
|
|
|
bool splitBigTiles( TileNode &tile, const NormalizedRect &rect );
|
2012-11-08 11:50:33 +00:00
|
|
|
|
|
|
|
// The page is split in a 4x4 grid of tiles
|
2012-11-08 15:41:00 +00:00
|
|
|
TileNode tiles[16];
|
2012-08-04 21:00:19 +00:00
|
|
|
int width;
|
|
|
|
int height;
|
2012-11-08 13:58:08 +00:00
|
|
|
int pageNumber;
|
2012-11-08 18:31:53 +00:00
|
|
|
qulonglong totalPixels;
|
2012-08-17 17:25:58 +00:00
|
|
|
Rotation rotation;
|
2012-08-20 01:43:59 +00:00
|
|
|
NormalizedRect visibleRect;
|
2012-09-25 01:01:47 +00:00
|
|
|
NormalizedRect requestRect;
|
|
|
|
int requestWidth;
|
|
|
|
int requestHeight;
|
2012-08-04 21:00:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TilesManager::Private::Private()
|
|
|
|
: width( 0 )
|
|
|
|
, height( 0 )
|
2012-11-08 13:58:08 +00:00
|
|
|
, pageNumber( 0 )
|
2012-08-04 21:00:19 +00:00
|
|
|
, totalPixels( 0 )
|
2012-08-17 17:25:58 +00:00
|
|
|
, rotation( Rotation0 )
|
2012-09-25 01:01:47 +00:00
|
|
|
, requestRect( NormalizedRect() )
|
|
|
|
, requestWidth( 0 )
|
|
|
|
, requestHeight( 0 )
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-11-08 13:58:08 +00:00
|
|
|
TilesManager::TilesManager( int pageNumber, int width, int height, Rotation rotation )
|
2012-08-04 21:00:19 +00:00
|
|
|
: d( new Private )
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2012-11-08 13:58:08 +00:00
|
|
|
d->pageNumber = pageNumber;
|
2012-08-04 21:00:19 +00:00
|
|
|
d->width = width;
|
|
|
|
d->height = height;
|
2012-08-17 17:25:58 +00:00
|
|
|
d->rotation = rotation;
|
2012-08-04 21:00:19 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// The page is split in a 4x4 grid of tiles
|
2012-07-11 05:14:50 +00:00
|
|
|
const double dim = 0.25;
|
2012-07-07 17:40:52 +00:00
|
|
|
for ( int i = 0; i < 16; ++i )
|
|
|
|
{
|
2012-07-11 05:14:50 +00:00
|
|
|
int x = i % 4;
|
|
|
|
int y = i / 4;
|
2012-08-04 21:00:19 +00:00
|
|
|
d->tiles[ i ].rect = NormalizedRect( x*dim, y*dim, x*dim+dim, y*dim+dim );
|
2012-07-07 17:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TilesManager::~TilesManager()
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < 16; ++i )
|
2012-08-04 21:00:19 +00:00
|
|
|
d->deleteTiles( d->tiles[ i ] );
|
2012-08-24 16:24:44 +00:00
|
|
|
|
|
|
|
delete d;
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
void TilesManager::Private::deleteTiles( const TileNode &tile )
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
|
|
|
if ( tile.pixmap )
|
2012-11-08 11:50:08 +00:00
|
|
|
{
|
|
|
|
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
|
2012-08-04 21:00:19 +00:00
|
|
|
delete tile.pixmap;
|
2012-11-08 11:50:08 +00:00
|
|
|
}
|
2012-08-04 21:00:19 +00:00
|
|
|
|
2012-08-24 16:24:44 +00:00
|
|
|
if ( tile.nTiles > 0 )
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
|
|
|
deleteTiles( tile.tiles[ i ] );
|
|
|
|
|
|
|
|
delete [] tile.tiles;
|
|
|
|
}
|
2012-07-11 05:14:50 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 19:56:39 +00:00
|
|
|
void TilesManager::setSize( int width, int height )
|
2012-07-11 05:14:50 +00:00
|
|
|
{
|
2012-11-11 19:56:39 +00:00
|
|
|
if ( width == d->width && height == d->height )
|
2012-07-11 05:14:50 +00:00
|
|
|
return;
|
|
|
|
|
2012-08-04 21:00:19 +00:00
|
|
|
d->width = width;
|
2012-11-11 19:56:39 +00:00
|
|
|
d->height = height;
|
2012-07-11 05:14:50 +00:00
|
|
|
|
2012-10-25 12:16:17 +00:00
|
|
|
markDirty();
|
2012-07-11 05:14:50 +00:00
|
|
|
}
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
int TilesManager::width() const
|
|
|
|
{
|
2012-08-04 21:00:19 +00:00
|
|
|
return d->width;
|
|
|
|
}
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
int TilesManager::height() const
|
|
|
|
{
|
2012-08-04 21:00:19 +00:00
|
|
|
return d->height;
|
|
|
|
}
|
|
|
|
|
2012-08-17 17:25:58 +00:00
|
|
|
void TilesManager::setRotation( Rotation rotation )
|
|
|
|
{
|
|
|
|
if ( rotation == d->rotation )
|
|
|
|
return;
|
|
|
|
|
|
|
|
d->rotation = rotation;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rotation TilesManager::rotation() const
|
|
|
|
{
|
|
|
|
return d->rotation;
|
|
|
|
}
|
|
|
|
|
2012-10-25 12:16:17 +00:00
|
|
|
void TilesManager::markDirty()
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < 16; ++i )
|
|
|
|
{
|
2012-11-08 11:50:33 +00:00
|
|
|
TilesManager::Private::markDirty( d->tiles[ i ] );
|
2012-10-25 12:16:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
void TilesManager::Private::markDirty( TileNode &tile )
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
|
|
|
tile.dirty = true;
|
|
|
|
|
2012-08-18 20:16:30 +00:00
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
2012-07-11 05:14:50 +00:00
|
|
|
{
|
2012-08-18 20:16:30 +00:00
|
|
|
markDirty( tile.tiles[ i ] );
|
2012-07-07 17:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-01 18:42:37 +00:00
|
|
|
void TilesManager::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, bool isPartialPixmap )
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2018-02-01 18:42:37 +00:00
|
|
|
const NormalizedRect rotatedRect = TilesManager::fromRotatedRect( rect, d->rotation );
|
2012-09-25 01:01:47 +00:00
|
|
|
if ( !d->requestRect.isNull() )
|
|
|
|
{
|
2012-11-15 20:19:57 +00:00
|
|
|
if ( !(d->requestRect == rect) )
|
|
|
|
return;
|
|
|
|
|
2018-02-01 18:42:37 +00:00
|
|
|
if ( pixmap )
|
2012-11-15 20:19:57 +00:00
|
|
|
{
|
2018-02-01 18:42:37 +00:00
|
|
|
// 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();
|
|
|
|
}
|
2012-11-15 20:19:57 +00:00
|
|
|
|
2018-02-01 18:42:37 +00:00
|
|
|
if ( rotatedRect.geometry( w, h ).size() != pixmapSize )
|
|
|
|
return;
|
|
|
|
}
|
2012-09-25 01:01:47 +00:00
|
|
|
|
|
|
|
d->requestRect = NormalizedRect();
|
|
|
|
}
|
|
|
|
|
2012-08-04 21:00:19 +00:00
|
|
|
for ( int i = 0; i < 16; ++i )
|
|
|
|
{
|
2018-02-01 18:42:37 +00:00
|
|
|
d->setPixmap( pixmap, rotatedRect, d->tiles[ i ], isPartialPixmap );
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-01 18:42:37 +00:00
|
|
|
void TilesManager::Private::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect, TileNode &tile, bool isPartialPixmap )
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
2012-08-17 17:25:58 +00:00
|
|
|
QRect pixmapRect = TilesManager::toRotatedRect( rect, rotation ).geometry( width, height );
|
2012-08-04 21:00:19 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// Exclude tiles outside the viewport
|
2012-08-04 21:00:19 +00:00
|
|
|
if ( !tile.rect.intersects( rect ) )
|
|
|
|
return;
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// 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
|
2012-08-04 21:00:19 +00:00
|
|
|
if ( !((tile.rect & rect) == tile.rect) )
|
|
|
|
{
|
2012-08-18 20:16:30 +00:00
|
|
|
// paint children tiles
|
|
|
|
if ( tile.nTiles > 0 )
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
2018-02-01 18:42:37 +00:00
|
|
|
setPixmap( pixmap, rect, tile.tiles[ i ], isPartialPixmap );
|
2012-08-18 20:16:30 +00:00
|
|
|
|
|
|
|
delete tile.pixmap;
|
2017-09-05 21:27:18 +00:00
|
|
|
tile.pixmap = nullptr;
|
2012-08-18 20:16:30 +00:00
|
|
|
}
|
2012-08-04 21:00:19 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// the tile lies entirely within the viewport
|
2012-08-04 21:00:19 +00:00
|
|
|
if ( tile.nTiles == 0 )
|
|
|
|
{
|
2018-02-01 18:42:37 +00:00
|
|
|
tile.dirty = isPartialPixmap;
|
2012-08-04 21:00:19 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// check whether the tile size is big and split it if necessary
|
|
|
|
if ( !splitBigTiles( tile, rect ) )
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
|
|
|
if ( tile.pixmap )
|
|
|
|
{
|
|
|
|
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
|
|
|
|
delete tile.pixmap;
|
|
|
|
}
|
2012-11-10 17:50:58 +00:00
|
|
|
tile.rotation = rotation;
|
2018-02-01 18:42:37 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-11-08 11:50:33 +00:00
|
|
|
if ( tile.pixmap )
|
2012-08-13 04:09:05 +00:00
|
|
|
{
|
2012-11-08 11:50:33 +00:00
|
|
|
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
|
|
|
|
delete tile.pixmap;
|
2017-09-05 21:27:18 +00:00
|
|
|
tile.pixmap = nullptr;
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
2012-11-08 11:50:33 +00:00
|
|
|
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
2018-02-01 18:42:37 +00:00
|
|
|
setPixmap( pixmap, rect, tile.tiles[ i ], isPartialPixmap );
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2012-08-04 21:00:19 +00:00
|
|
|
QRect tileRect = tile.rect.geometry( width, height );
|
2012-11-08 11:50:33 +00:00
|
|
|
// 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 )
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
2018-02-01 18:42:37 +00:00
|
|
|
tile.dirty = isPartialPixmap;
|
2012-08-18 20:16:30 +00:00
|
|
|
if ( tile.pixmap )
|
|
|
|
{
|
|
|
|
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
|
|
|
|
delete tile.pixmap;
|
2017-09-05 21:27:18 +00:00
|
|
|
tile.pixmap = nullptr;
|
2012-08-18 20:16:30 +00:00
|
|
|
}
|
2012-11-08 11:50:33 +00:00
|
|
|
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
2018-02-01 18:42:37 +00:00
|
|
|
setPixmap( pixmap, rect, tile.tiles[ i ], isPartialPixmap );
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
2012-11-08 11:50:33 +00:00
|
|
|
else
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2012-08-04 21:00:19 +00:00
|
|
|
// remove children tiles
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
|
|
|
{
|
2012-11-08 11:50:08 +00:00
|
|
|
deleteTiles( tile.tiles[ i ] );
|
2017-09-05 21:27:18 +00:00
|
|
|
tile.tiles[ i ].pixmap = nullptr;
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
2012-07-07 17:40:52 +00:00
|
|
|
|
2012-08-04 21:00:19 +00:00
|
|
|
delete [] tile.tiles;
|
2017-09-05 21:27:18 +00:00
|
|
|
tile.tiles = nullptr;
|
2012-08-04 21:00:19 +00:00
|
|
|
tile.nTiles = 0;
|
|
|
|
|
|
|
|
// paint tile
|
|
|
|
if ( tile.pixmap )
|
|
|
|
{
|
|
|
|
totalPixels -= tile.pixmap->width()*tile.pixmap->height();
|
|
|
|
delete tile.pixmap;
|
|
|
|
}
|
2012-11-10 17:50:58 +00:00
|
|
|
tile.rotation = rotation;
|
2018-02-01 18:42:37 +00:00
|
|
|
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;
|
2012-07-07 17:40:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TilesManager::hasPixmap( const NormalizedRect &rect )
|
|
|
|
{
|
2012-11-08 11:50:33 +00:00
|
|
|
NormalizedRect rotatedRect = fromRotatedRect( rect, d->rotation );
|
2012-08-04 21:00:19 +00:00
|
|
|
for ( int i = 0; i < 16; ++i )
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2012-11-08 11:50:33 +00:00
|
|
|
if ( !d->hasPixmap( rotatedRect, d->tiles[ i ] ) )
|
2012-08-04 21:00:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
2012-07-07 17:40:52 +00:00
|
|
|
|
2012-08-04 21:00:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
bool TilesManager::Private::hasPixmap( const NormalizedRect &rect, const TileNode &tile ) const
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
2018-02-01 18:42:37 +00:00
|
|
|
const NormalizedRect rectIntersection = tile.rect & rect;
|
|
|
|
if ( rectIntersection.width() <= 0 || rectIntersection.height() <= 0 )
|
2012-08-04 21:00:19 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if ( tile.nTiles == 0 )
|
2012-11-08 11:50:33 +00:00
|
|
|
return tile.isValid();
|
2012-08-04 21:00:19 +00:00
|
|
|
|
2012-08-13 04:09:05 +00:00
|
|
|
// all children tiles are clean. doesn't need to go deeper
|
2012-08-04 21:00:19 +00:00
|
|
|
if ( !tile.dirty )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
|
|
|
{
|
|
|
|
if ( !hasPixmap( rect, tile.tiles[ i ] ) )
|
|
|
|
return false;
|
2012-07-07 17:40:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-11-12 14:55:13 +00:00
|
|
|
QList<Tile> TilesManager::tilesAt( const NormalizedRect &rect, TileLeaf tileLeaf )
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2012-07-11 05:14:50 +00:00
|
|
|
QList<Tile> result;
|
2012-07-07 17:40:52 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
NormalizedRect rotatedRect = fromRotatedRect( rect, d->rotation );
|
2012-08-04 21:00:19 +00:00
|
|
|
for ( int i = 0; i < 16; ++i )
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
2012-11-12 14:55:13 +00:00
|
|
|
d->tilesAt( rotatedRect, d->tiles[ i ], result, tileLeaf );
|
2012-07-07 17:40:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-11-12 14:55:13 +00:00
|
|
|
void TilesManager::Private::tilesAt( const NormalizedRect &rect, TileNode &tile, QList<Tile> &result, TileLeaf tileLeaf )
|
2012-07-16 15:57:51 +00:00
|
|
|
{
|
2012-08-04 21:00:19 +00:00
|
|
|
if ( !tile.rect.intersects( rect ) )
|
|
|
|
return;
|
2012-07-16 15:57:51 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// split big tiles before the requests are made, otherwise we would end up
|
|
|
|
// requesting huge areas unnecessarily
|
|
|
|
splitBigTiles( tile, rect );
|
2012-08-18 20:16:30 +00:00
|
|
|
|
2012-11-12 14:55:13 +00:00
|
|
|
if ( ( tileLeaf == TerminalTile && tile.nTiles == 0 ) || ( tileLeaf == PixmapTile && tile.pixmap ) )
|
2012-07-16 15:57:51 +00:00
|
|
|
{
|
2012-11-08 15:41:00 +00:00
|
|
|
NormalizedRect rotatedRect;
|
2012-08-17 17:25:58 +00:00
|
|
|
if ( rotation != Rotation0 )
|
2012-11-08 15:41:00 +00:00
|
|
|
rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation );
|
|
|
|
else
|
|
|
|
rotatedRect = tile.rect;
|
2012-11-10 17:50:58 +00:00
|
|
|
|
2012-11-12 14:55:13 +00:00
|
|
|
if ( tile.pixmap && tileLeaf == PixmapTile && tile.rotation != rotation )
|
2012-11-10 17:50:58 +00:00
|
|
|
{
|
|
|
|
// 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;
|
|
|
|
}
|
2012-11-08 15:41:00 +00:00
|
|
|
result.append( Tile( rotatedRect, tile.pixmap, tile.isValid() ) );
|
2012-07-16 15:57:51 +00:00
|
|
|
}
|
2012-08-04 21:00:19 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
2012-11-12 14:55:13 +00:00
|
|
|
tilesAt( rect, tile.tiles[ i ], result, tileLeaf );
|
2012-08-04 21:00:19 +00:00
|
|
|
}
|
|
|
|
}
|
2012-07-16 15:57:51 +00:00
|
|
|
|
2012-11-08 18:31:53 +00:00
|
|
|
qulonglong TilesManager::totalMemory() const
|
2012-08-04 21:00:19 +00:00
|
|
|
{
|
|
|
|
return 4*d->totalPixels;
|
2012-07-16 15:57:51 +00:00
|
|
|
}
|
|
|
|
|
2012-11-08 13:58:08 +00:00
|
|
|
void TilesManager::cleanupPixmapMemory( qulonglong numberOfBytes, const NormalizedRect &visibleRect, int visiblePageNumber )
|
2012-08-13 04:09:05 +00:00
|
|
|
{
|
2012-11-08 15:41:00 +00:00
|
|
|
QList<TileNode*> rankedTiles;
|
2012-08-13 04:09:05 +00:00
|
|
|
for ( int i = 0; i < 16; ++i )
|
|
|
|
{
|
2012-11-08 13:58:08 +00:00
|
|
|
d->rankTiles( d->tiles[ i ], rankedTiles, visibleRect, visiblePageNumber );
|
2012-08-13 04:09:05 +00:00
|
|
|
}
|
2012-11-08 11:50:33 +00:00
|
|
|
qSort( rankedTiles.begin(), rankedTiles.end(), rankedTilesLessThan );
|
2012-08-13 04:09:05 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
while ( numberOfBytes > 0 && !rankedTiles.isEmpty() )
|
2012-08-13 04:09:05 +00:00
|
|
|
{
|
2012-11-08 15:41:00 +00:00
|
|
|
TileNode *tile = rankedTiles.takeLast();
|
2012-08-13 04:09:05 +00:00
|
|
|
if ( !tile->pixmap )
|
|
|
|
continue;
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
// do not evict visible pixmaps
|
2012-11-08 13:58:08 +00:00
|
|
|
if ( tile->rect.intersects( visibleRect ) )
|
2012-08-20 01:43:59 +00:00
|
|
|
continue;
|
|
|
|
|
2012-08-26 17:36:21 +00:00
|
|
|
qulonglong pixels = tile->pixmap->width()*tile->pixmap->height();
|
2012-08-13 04:09:05 +00:00
|
|
|
d->totalPixels -= pixels;
|
2012-08-15 18:23:29 +00:00
|
|
|
if ( numberOfBytes < 4*pixels )
|
|
|
|
numberOfBytes = 0;
|
|
|
|
else
|
|
|
|
numberOfBytes -= 4*pixels;
|
2012-08-13 04:09:05 +00:00
|
|
|
|
|
|
|
delete tile->pixmap;
|
2017-09-05 21:27:18 +00:00
|
|
|
tile->pixmap = nullptr;
|
2012-08-13 04:09:05 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
d->markParentDirty( *tile );
|
2012-08-13 04:09:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
void TilesManager::Private::markParentDirty( const TileNode &tile )
|
2012-08-13 04:09:05 +00:00
|
|
|
{
|
|
|
|
if ( !tile.parent )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( !tile.parent->dirty )
|
|
|
|
{
|
|
|
|
tile.parent->dirty = true;
|
2012-11-08 11:50:33 +00:00
|
|
|
markParentDirty( *tile.parent );
|
2012-08-13 04:09:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
void TilesManager::Private::rankTiles( TileNode &tile, QList<TileNode*> &rankedTiles, const NormalizedRect &visibleRect, int visiblePageNumber )
|
2012-08-13 04:09:05 +00:00
|
|
|
{
|
2012-11-08 13:58:08 +00:00
|
|
|
// 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;
|
2012-08-13 04:09:05 +00:00
|
|
|
|
|
|
|
if ( tile.pixmap )
|
|
|
|
{
|
2012-11-08 13:58:08 +00:00
|
|
|
// Update distance
|
|
|
|
if ( !visibleRect.isNull() )
|
|
|
|
{
|
|
|
|
NormalizedPoint viewportCenter = visibleRect.center();
|
|
|
|
NormalizedPoint tileCenter = tile.rect.center();
|
|
|
|
// Manhattan distance. It's a good and fast approximation.
|
2012-11-08 18:12:05 +00:00
|
|
|
tile.distance = qAbs(viewportCenter.x - tileCenter.x) + qAbs(viewportCenter.y - tileCenter.y);
|
2012-11-08 13:58:08 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2012-08-13 04:09:05 +00:00
|
|
|
rankedTiles.append( &tile );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
|
|
|
{
|
2012-11-08 13:58:08 +00:00
|
|
|
rankTiles( tile.tiles[ i ], rankedTiles, visibleRect, visiblePageNumber );
|
2012-08-13 04:09:05 +00:00
|
|
|
}
|
2012-08-18 20:16:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-25 01:01:47 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
bool TilesManager::Private::splitBigTiles( TileNode &tile, const NormalizedRect &rect )
|
2012-11-08 11:50:33 +00:00
|
|
|
{
|
|
|
|
QRect tileRect = tile.rect.geometry( width, height );
|
|
|
|
if ( tileRect.width()*tileRect.height() < TILES_MAXSIZE )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
split( tile, rect );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
void TilesManager::Private::split( TileNode &tile, const NormalizedRect &rect )
|
2012-08-18 20:16:30 +00:00
|
|
|
{
|
|
|
|
if ( tile.nTiles != 0 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( rect.isNull() || !tile.rect.intersects( rect ) )
|
|
|
|
return;
|
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
tile.nTiles = 4;
|
2012-11-08 15:41:00 +00:00
|
|
|
tile.tiles = new TileNode[4];
|
2012-11-08 11:50:33 +00:00
|
|
|
double hCenter = (tile.rect.left + tile.rect.right)/2;
|
|
|
|
double vCenter = (tile.rect.top + tile.rect.bottom)/2;
|
2012-08-18 20:16:30 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
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 );
|
2012-08-18 20:16:30 +00:00
|
|
|
|
2012-11-08 11:50:33 +00:00
|
|
|
for ( int i = 0; i < tile.nTiles; ++i )
|
|
|
|
{
|
|
|
|
tile.tiles[ i ].parent = &tile;
|
|
|
|
splitBigTiles( tile.tiles[ i ], rect );
|
2012-08-13 04:09:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-17 17:25:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
TileNode::TileNode()
|
2017-09-05 21:27:18 +00:00
|
|
|
: pixmap( nullptr )
|
2012-11-10 17:50:58 +00:00
|
|
|
, rotation( Rotation0 )
|
2012-07-11 05:14:50 +00:00
|
|
|
, dirty ( true )
|
2012-11-08 13:58:08 +00:00
|
|
|
, distance( -1 )
|
2017-09-05 21:27:18 +00:00
|
|
|
, tiles( nullptr )
|
2012-08-04 21:00:19 +00:00
|
|
|
, nTiles( 0 )
|
2017-09-05 21:27:18 +00:00
|
|
|
, parent( nullptr )
|
2012-07-07 17:40:52 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
bool TileNode::isValid() const
|
2012-07-11 05:14:50 +00:00
|
|
|
{
|
|
|
|
return pixmap && !dirty;
|
2012-07-07 17:40:52 +00:00
|
|
|
}
|
2012-11-08 15:41:00 +00:00
|
|
|
|
|
|
|
class Tile::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Private();
|
|
|
|
|
|
|
|
NormalizedRect rect;
|
|
|
|
QPixmap *pixmap;
|
|
|
|
bool isValid;
|
|
|
|
};
|
|
|
|
|
|
|
|
Tile::Private::Private()
|
2017-09-05 21:27:18 +00:00
|
|
|
: pixmap( nullptr )
|
2012-11-08 15:41:00 +00:00
|
|
|
, 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;
|
|
|
|
}
|
|
|
|
|
2012-11-10 04:54:09 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-11-08 15:41:00 +00:00
|
|
|
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;
|
|
|
|
}
|