From 9f3cf45e37ff9a9ff21913e847acfd69390c03cc Mon Sep 17 00:00:00 2001 From: Mailson Menezes Date: Sat, 7 Jul 2012 14:40:52 -0300 Subject: [PATCH] Rendering with a tiles manager Currently it's only available for PDFs and does not support rotation. --- CMakeLists.txt | 1 + core/page.cpp | 51 ++++++++++++++++ core/page.h | 11 ++++ core/page_p.h | 2 + core/tilesmanager.cpp | 132 ++++++++++++++++++++++++++++++++++++++++++ core/tilesmanager.h | 42 ++++++++++++++ ui/pagepainter.cpp | 70 +++++++++++++++------- ui/pageview.cpp | 25 ++++++-- 8 files changed, 310 insertions(+), 24 deletions(-) create mode 100644 core/tilesmanager.cpp create mode 100644 core/tilesmanager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e4cb88f53..74c06cda1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ set(okularcore_SRCS core/sourcereference.cpp core/textdocumentgenerator.cpp core/textpage.cpp + core/tilesmanager.cpp core/utils.cpp core/view.cpp core/fileprinter.cpp diff --git a/core/page.cpp b/core/page.cpp index b0b2c7404..26546cced 100644 --- a/core/page.cpp +++ b/core/page.cpp @@ -134,6 +134,7 @@ Page::Page( uint page, double w, double h, Rotation o ) Page::~Page() { deletePixmaps(); + deleteTilesManagers(); deleteRects(); d->deleteHighlights(); deleteAnnotations(); @@ -203,6 +204,26 @@ void Page::setBoundingBox( const NormalizedRect& bbox ) bool Page::hasPixmap( int id, int width, int height, const NormalizedRect &rect ) const { + if ( id == 3 ) + { + TilesManager *tm = tilesManager( id ); + if ( !tm ) + { + tm = new TilesManager( width, height ); + d->m_tilesManagers.insert( id, tm ); + } + + // TODO: mark tiles as dirty + if ( width != tm->width || height != tm->height ) + { + tm->width = width; + tm->height = height; + return false; + } + + return tm->hasPixmap( rect ); + } + QMap< int, PagePrivate::PixmapObject >::const_iterator it = d->m_pixmaps.constFind( id ); if ( it == d->m_pixmaps.constEnd() ) return false; @@ -467,6 +488,13 @@ QLinkedList< FormField * > Page::formFields() const void Page::setPixmap( int id, QPixmap *pixmap, const NormalizedRect &rect ) { + TilesManager *tm = tilesManager( id ); + if ( tm ) + { + tm->setPixmap( pixmap, rect ); + return; + } + if ( d->m_rotation == Rotation0 ) { QMap< int, PagePrivate::PixmapObject >::iterator it = d->m_pixmaps.find( id ); if ( it != d->m_pixmaps.end() ) @@ -922,3 +950,26 @@ const QPixmap * Page::_o_nearestPixmap( int pixID, int w, int h ) const return pixmap; } + +TilesManager *Page::tilesManager( int id ) const +{ + TilesManager *tilesManager = 0; + + QMap< int, TilesManager* >::iterator itTilesManager = d->m_tilesManagers.find( id ); + if ( itTilesManager != d->m_tilesManagers.end() ) + tilesManager = *itTilesManager; + + return tilesManager; +} + +void Page::deleteTilesManagers() +{ + QMap< int, TilesManager* >::iterator itTilesManager = d->m_tilesManagers.begin(); + while ( itTilesManager != d->m_tilesManagers.end() ) + { + delete (*itTilesManager); + itTilesManager++; + } + + d->m_tilesManagers.clear(); +} diff --git a/core/page.h b/core/page.h index 19321609c..aaad9d836 100644 --- a/core/page.h +++ b/core/page.h @@ -31,6 +31,7 @@ class PagePrivate; class PageTransition; class SourceReference; class TextSelection; +class TilesManager; /** * @short Collector for all the data belonging to a page. @@ -357,6 +358,16 @@ class OKULAR_EXPORT Page */ void deleteAnnotations(); + /** + * Returns the tile manager for the observer with the given @p id. + */ + TilesManager *tilesManager( int id ) const; + + /** + * Deletes all tiles managers + */ + void deleteTilesManagers(); + private: PagePrivate* const d; /// @cond PRIVATE diff --git a/core/page_p.h b/core/page_p.h index 3ad435cdd..5237b83e6 100644 --- a/core/page_p.h +++ b/core/page_p.h @@ -20,6 +20,7 @@ // local includes #include "global.h" #include "area.h" +#include "tilesmanager.h" class QColor; class QDomDocument; @@ -107,6 +108,7 @@ class PagePrivate Rotation m_rotation; }; QMap< int, PixmapObject > m_pixmaps; + QMap< int, TilesManager* > m_tilesManagers; Page *m_page; int m_number; diff --git a/core/tilesmanager.cpp b/core/tilesmanager.cpp new file mode 100644 index 000000000..d494d3a6c --- /dev/null +++ b/core/tilesmanager.cpp @@ -0,0 +1,132 @@ +#include "tilesmanager.h" + +#include +#include + +using namespace Okular; + +TilesManager::TilesManager( int width, int height ) + : width( width ), height( height ) +{ + for ( int i = 0; i < 16; ++i ) + { + mTiles[ i ] = 0; + } +} + +TilesManager::~TilesManager() +{ + for ( int i = 0; i < 16; ++i ) + { + if ( mTiles[ i ] ) + { + delete mTiles[ i ]; + mTiles[ i ] = 0; + } + } +} + +void TilesManager::setPixmap( QPixmap *pixmap, const NormalizedRect &rect ) +{ + const double dim = 0.25; + int left = qCeil( rect.left/dim ); + int top = qCeil( rect.top/dim ); + int right = rect.right/dim; + int bottom = rect.bottom/dim; + + const QRect pixmapRect = rect.geometry( width, height ); + + for ( int y = top; y < bottom; y++ ) + { + for ( int x = left; x < right; x++ ) + { + const NormalizedRect tileRect( x*dim, y*dim, x*dim+dim, y*dim+dim ); + int index = 4*y+x; + if ( !mTiles[ index ] ) + mTiles[ index ] = new Tile(); + + Tile *tile = mTiles[ index ]; + tile->rect = tileRect; + if ( tile->pixmap ) + delete (tile->pixmap); + + tile->pixmap = new QPixmap( pixmap->copy( tileRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) ); + } + } + + delete pixmap; +} + +bool TilesManager::hasPixmap( const NormalizedRect &rect ) +{ + const double dim = 0.25; + int left = rect.left/dim; + int top = rect.top/dim; + int right = qCeil( rect.right/dim ); + int bottom = qCeil( rect.bottom/dim ); + + for ( int y = top; y < bottom; y++ ) + { + for ( int x = left; x < right; x++ ) + { + int index = 4*y + x; + if ( !mTiles[ index ] ) + return false; + + if ( !mTiles[ index ]->pixmap ) + return false; + + const NormalizedRect tileRect( x*dim, y*dim, x*dim+dim, y*dim+dim ); + QRect tileQRect = tileRect.geometry( width, height ); + if ( tileQRect.width() != mTiles[ index ]->pixmap->width() ) + return false; + if ( tileQRect.height() != mTiles[ index ]->pixmap->height() ) + return false; + } + } + + return true; +} + +QList TilesManager::tilesAt( const NormalizedRect &rect ) const +{ + QList result; + + const double dim = 0.25; + int left = rect.left/dim; + int top = rect.top/dim; + int right = qCeil( rect.right/dim ); + int bottom = qCeil( rect.bottom/dim ); + + for ( int y = top; y < bottom; y++ ) + { + for ( int x = left; x < right; x++ ) + { + int index = 4*y + x; + if ( mTiles[ index ] ) + { + if ( !mTiles[ index ]->pixmap ) + continue; + + const NormalizedRect tileRect( x*dim, y*dim, x*dim+dim, y*dim+dim ); + QRect tileQRect = tileRect.geometry( width, height ); + if ( tileQRect.width() == mTiles[ index ]->pixmap->width() + && tileQRect.height() == mTiles[ index ]->pixmap->height() ) + { + result.append( mTiles[ index ] ); + } + } + } + } + + return result; +} + +Tile::Tile() + : pixmap( 0 ) +{ +} + +Tile::~Tile() { + delete pixmap; +} diff --git a/core/tilesmanager.h b/core/tilesmanager.h new file mode 100644 index 000000000..39cefbafa --- /dev/null +++ b/core/tilesmanager.h @@ -0,0 +1,42 @@ +#ifndef _OKULAR_TILES_MANAGER_H_ +#define _OKULAR_TILES_MANAGER_H_ + +#include "okular_export.h" +#include "area.h" + +class QPixmap; + +namespace Okular { + +class Tile; + +class OKULAR_EXPORT TilesManager +{ + public: + TilesManager( int width, int height ); + virtual ~TilesManager(); + + void setPixmap( QPixmap *pixmap, const NormalizedRect &rect ); + bool hasPixmap( const NormalizedRect &rect ); + QList tilesAt( const NormalizedRect &rect ) const; + + int width; + int height; + + private: + Tile *mTiles[16]; +}; + +class OKULAR_EXPORT Tile +{ + public: + Tile(); + virtual ~Tile(); + + NormalizedRect rect; + QPixmap *pixmap; +}; + +} + +#endif // _OKULAR_TILES_MANAGER_H_ diff --git a/ui/pagepainter.cpp b/ui/pagepainter.cpp index bdb9c5a20..8fe998f94 100644 --- a/ui/pagepainter.cpp +++ b/ui/pagepainter.cpp @@ -30,6 +30,7 @@ #include "core/utils.h" #include "guiutils.h" #include "settings.h" +#include "core/tilesmanager.h" K_GLOBAL_STATIC_WITH_ARGS( QPixmap, busyPixmap, ( KIconLoader::global()->loadIcon("okular", KIconLoader::NoGroup, 32, KIconLoader::DefaultState, QStringList(), 0, true) ) ) @@ -63,9 +64,6 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula int croppedWidth = scaledCrop.width(); int croppedHeight = scaledCrop.height(); - /** 1 - RETRIEVE THE 'PAGE+ID' PIXMAP OR A SIMILAR 'PAGE' ONE **/ - const QPixmap * pixmap = page->_o_nearestPixmap( pixID, scaledWidth, scaledHeight ); - QColor color = Qt::white; if ( Okular::Settings::changeColors() ) { @@ -85,24 +83,32 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula } destPainter->fillRect( limits, color ); - /** 1B - IF NO PIXMAP, DRAW EMPTY PAGE **/ - double pixmapRescaleRatio = pixmap ? limits.width() / (double)pixmap->width() : -1; - long pixmapPixels = pixmap ? (long)pixmap->width() * (long)pixmap->height() : 0; - if ( !pixmap || pixmapRescaleRatio > 20.0 || pixmapRescaleRatio < 0.25 || - (limits.width() != pixmap->width() && pixmapPixels > 6000000L) ) + Okular::TilesManager *tilesManager = page->tilesManager( pixID ); + const QPixmap *pixmap = 0; + if ( !tilesManager ) { - // draw something on the blank page: the okular icon or a cross (as a fallback) - if ( !busyPixmap->isNull() ) + /** 1 - RETRIEVE THE 'PAGE+ID' PIXMAP OR A SIMILAR 'PAGE' ONE **/ + pixmap = page->_o_nearestPixmap( pixID, 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) ) { - 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 ); + // 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; } - return; } /** 2 - FIND OUT WHAT TO PAINT (Flags + Configuration + Presence) **/ @@ -230,8 +236,32 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula /** 4A -- REGULAR FLOW. PAINT PIXMAP NORMAL OR RESCALED USING GIVEN QPAINTER **/ if ( !useBackBuffer ) { - // 4A.1. if size is ok, draw the page pixmap using painter - destPainter->drawPixmap( limits.topLeft(), *pixmap ); + if ( tilesManager ) + { + QList tiles = tilesManager->tilesAt( crop ); + QList::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd(); + while ( tIt != tEnd ) + { + Okular::Tile *tile = *tIt; + destPainter->drawPixmap( tile->rect.geometry( scaledWidth, scaledHeight ).topLeft(), *(tile->pixmap) ); + tIt++; + } + } + 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() ); + } + } // 4A.2. active painter is the one passed to this method mixedPainter = destPainter; diff --git a/ui/pageview.cpp b/ui/pageview.cpp index b629b7acc..cced04ea4 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -3983,10 +3983,27 @@ void PageView::slotRequestVisiblePixmaps( int newValue ) #ifdef PAGEVIEW_DEBUG kWarning() << "rerequesting visible pixmaps for page" << i->pageNumber() << "!"; #endif - Okular::PixmapRequest * p = new Okular::PixmapRequest( - PAGEVIEW_ID, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRIO, true ); - p->setNormalizedRect( vItem->rect ); - requestedPixmaps.push_back( p ); + const double dim = 0.25; + int left = vItem->rect.left/dim; + int top = vItem->rect.top/dim; + int right = ceil( vItem->rect.right/dim ); + int bottom = ceil( vItem->rect.bottom/dim ); + + for ( int y = top; y < bottom; y++ ) + { + for ( int x = left; x < right; x++ ) + { + const Okular::NormalizedRect tileRect( x*dim, y*dim, x*dim+dim, y*dim+dim ); + + if ( !i->page()->hasPixmap( PAGEVIEW_ID, i->uncroppedWidth(), i->uncroppedHeight(), tileRect ) ) + { + Okular::PixmapRequest * p = new Okular::PixmapRequest( + PAGEVIEW_ID, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRIO, true ); + p->setNormalizedRect( tileRect ); + requestedPixmaps.push_back( p ); + } + } + } } // look for the item closest to viewport center and the relative