diff --git a/autotests/documenttest.cpp b/autotests/documenttest.cpp --- a/autotests/documenttest.cpp +++ b/autotests/documenttest.cpp @@ -47,7 +47,7 @@ // Request a pixmap. A RotationJob will be enqueued but not started Okular::PixmapRequest *pixmapReq = new Okular::PixmapRequest( - dummyDocumentObserver, 0, 100, 100, 1, Okular::PixmapRequest::NoFeature ); + dummyDocumentObserver, 0, 100, 100, 1.0, 1, Okular::PixmapRequest::NoFeature ); m_document->requestPixmaps( QLinkedList() << pixmapReq ); // Delete the document diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -1313,10 +1313,10 @@ << " (" << 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() ); + const QPixmap *pixmap = r->page()->_o_nearestPixmap( r->observer(), r->width(), r->height(), r->devicePixelRatio() ); if ( pixmap ) { - tilesManager = new TilesManager( r->pageNumber(), pixmap->width(), pixmap->height(), r->page()->rotation() ); + tilesManager = new TilesManager( r->pageNumber(), pixmap->width() / pixmap->devicePixelRatioF(), pixmap->height() / pixmap->devicePixelRatioF(), r->page()->rotation() ); tilesManager->setPixmap( pixmap, NormalizedRect( 0, 0, 1, 1 ) ); tilesManager->setSize( r->width(), r->height() ); } @@ -1525,7 +1525,9 @@ for ( ; it != itEnd; ++it ) { QSize size = (*it).m_pixmap->size(); - PixmapRequest * p = new PixmapRequest( it.key(), pageNumber, size.width(), size.height(), 1, PixmapRequest::Asynchronous ); + qreal dpr = (*it).m_pixmap->devicePixelRatioF(); + + PixmapRequest * p = new PixmapRequest( it.key(), pageNumber, size.width() / dpr, size.height() / dpr, dpr, 1, PixmapRequest::Asynchronous ); p->d->mForce = true; requestedPixmaps.push_back( p ); } @@ -1537,7 +1539,7 @@ { tilesManager->markDirty(); - PixmapRequest * p = new PixmapRequest( observer, pageNumber, tilesManager->width(), tilesManager->height(), 1, PixmapRequest::Asynchronous ); + PixmapRequest * p = new PixmapRequest( observer, pageNumber, tilesManager->width(), tilesManager->height(), tilesManager->devicePixelRatio(), 1, PixmapRequest::Asynchronous ); NormalizedRect tilesRect; diff --git a/core/generator.h b/core/generator.h --- a/core/generator.h +++ b/core/generator.h @@ -617,10 +617,11 @@ * @param pageNumber The page number. * @param width The width of the page. * @param height The height of the page. + * @param dpr The device pixel ratio of the page. * @param priority The priority of the request. * @param features The features of generation. */ - PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features ); + PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, qreal dpr, int priority, PixmapRequestFeatures features ); /** * Destroys the pixmap request. @@ -648,6 +649,13 @@ int height() const; /** + * Returns the device pixel ratio of the requested pixmap. + * + * @since 1.2 + */ + qreal devicePixelRatio() const; + + /** * Returns the priority (less it better, 0 is maximum) of the * request. */ diff --git a/core/generator.cpp b/core/generator.cpp --- a/core/generator.cpp +++ b/core/generator.cpp @@ -102,7 +102,9 @@ } const QImage& img = mPixmapGenerationThread->image(); - request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( img ) ), request->normalizedRect() ); + QPixmap *p = new QPixmap( QPixmap::fromImage( img ) ); + p->setDevicePixelRatio( p->devicePixelRatioF() ); + request->page()->setPixmap( request->observer(), p, request->normalizedRect() ); const int pageNumber = request->page()->number(); if ( mPixmapGenerationThread->calcBoundingBox() ) @@ -258,7 +260,9 @@ } const QImage& img = image( request ); - request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( img ) ), request->normalizedRect() ); + QPixmap *p = new QPixmap( QPixmap::fromImage( img ) ); + p->setDevicePixelRatio( img.devicePixelRatioF() ); + request->page()->setPixmap( request->observer(), p , request->normalizedRect() ); const int pageNumber = request->page()->number(); d->mPixmapReady = true; @@ -284,7 +288,9 @@ QImage Generator::image( PixmapRequest *request ) { Q_D( Generator ); - return d->image( request ); + QImage image = d->image( request ); + image.setDevicePixelRatio( request->devicePixelRatio() ); + return image; } TextPage* Generator::textPage( Page* ) @@ -485,13 +491,14 @@ return 0; } -PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features ) +PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, qreal dpr, int priority, PixmapRequestFeatures features ) : d( new PixmapRequestPrivate ) { d->mObserver = observer; d->mPageNumber = pageNumber; - d->mWidth = width; - d->mHeight = height; + d->mWidth = width * dpr; + d->mHeight = height * dpr; + d->mDpr = dpr; d->mPriority = priority; d->mFeatures = features; d->mForce = false; @@ -524,6 +531,11 @@ return d->mHeight; } +qreal PixmapRequest::devicePixelRatio() const +{ + return d->mDpr; +} + int PixmapRequest::priority() const { return d->mPriority; diff --git a/core/generator_p.h b/core/generator_p.h --- a/core/generator_p.h +++ b/core/generator_p.h @@ -79,6 +79,7 @@ int mPageNumber; int mWidth; int mHeight; + qreal mDpr; int mPriority; int mFeatures; bool mForce : 1; diff --git a/core/page.h b/core/page.h --- a/core/page.h +++ b/core/page.h @@ -400,7 +400,7 @@ friend class ::PagePainter; /// @endcond - const QPixmap * _o_nearestPixmap( DocumentObserver *, int, int ) const; + const QPixmap * _o_nearestPixmap( DocumentObserver *, int, int, qreal ) const; QLinkedList< ObjectRect* > m_rects; QLinkedList< HighlightAreaRect* > m_highlights; diff --git a/core/page.cpp b/core/page.cpp --- a/core/page.cpp +++ b/core/page.cpp @@ -964,24 +964,26 @@ parentNode.appendChild( pageElement ); } -const QPixmap * Page::_o_nearestPixmap( DocumentObserver *observer, int w, int h ) const +const QPixmap * Page::_o_nearestPixmap( DocumentObserver *observer, int w, int h, qreal dpr ) const { Q_UNUSED( h ) - const QPixmap * pixmap = 0; + QPixmap * pixmap = 0; // if a pixmap is present for given id, use it QMap< DocumentObserver*, PagePrivate::PixmapObject >::const_iterator itPixmap = d->m_pixmaps.constFind( observer ); - if ( itPixmap != d->m_pixmaps.constEnd() ) + if ( itPixmap != d->m_pixmaps.constEnd() ) { pixmap = itPixmap.value().m_pixmap; + pixmap->setDevicePixelRatio(dpr); // else find the closest match using pixmaps of other IDs (great optim!) + } else if ( !d->m_pixmaps.isEmpty() ) { int minDistance = -1; QMap< DocumentObserver*, PagePrivate::PixmapObject >::const_iterator it = d->m_pixmaps.constBegin(), end = d->m_pixmaps.constEnd(); for ( ; it != end; ++it ) { - int pixWidth = (*it).m_pixmap->width(), + int pixWidth = (*it).m_pixmap->width() / (*it).m_pixmap->devicePixelRatioF(), distance = pixWidth > w ? pixWidth - w : w - pixWidth; if ( minDistance == -1 || distance < minDistance ) { diff --git a/core/tilesmanager.cpp b/core/tilesmanager.cpp --- a/core/tilesmanager.cpp +++ b/core/tilesmanager.cpp @@ -69,6 +69,7 @@ TileNode tiles[16]; int width; int height; + qreal dpr; int pageNumber; qulonglong totalPixels; Rotation rotation; @@ -81,6 +82,7 @@ TilesManager::Private::Private() : width( 0 ) , height( 0 ) + , dpr ( 1.0 ) , pageNumber( 0 ) , totalPixels( 0 ) , rotation( Rotation0 ) @@ -90,12 +92,13 @@ { } -TilesManager::TilesManager( int pageNumber, int width, int height, Rotation rotation ) +TilesManager::TilesManager( int pageNumber, int width, int height, qreal dpr, Rotation rotation ) : d( new Private ) { d->pageNumber = pageNumber; d->width = width; d->height = height; + d->dpr = dpr; d->rotation = rotation; // The page is split in a 4x4 grid of tiles @@ -154,6 +157,11 @@ return d->height; } +qreal TilesManager::devicePixelRatio() const +{ + return d->dpr; +} + void TilesManager::setRotation( Rotation rotation ) { if ( rotation == d->rotation ) @@ -199,7 +207,7 @@ // 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(); + QSize pixmapSize = pixmap->size() / pixmap->devicePixelRatioF(); int w = width(); int h = height(); if ( d->rotation % 2 ) @@ -259,7 +267,8 @@ delete tile.pixmap; } NormalizedRect rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation ); - tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) ); + tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width * pixmap->devicePixelRatioF(), height * pixmap->devicePixelRatioF() ).translated( -pixmapRect.topLeft() ) ) ); + tile.pixmap->setDevicePixelRatio(pixmap->devicePixelRatioF()); tile.rotation = rotation; totalPixels += tile.pixmap->width()*tile.pixmap->height(); } @@ -314,7 +323,8 @@ delete tile.pixmap; } NormalizedRect rotatedRect = TilesManager::toRotatedRect( tile.rect, rotation ); - tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width, height ).translated( -pixmapRect.topLeft() ) ) ); + tile.pixmap = new QPixmap( pixmap->copy( rotatedRect.geometry( width * pixmap->devicePixelRatioF(), height * pixmap->devicePixelRatioF() ).translated( -pixmapRect.topLeft() ) ) ); + tile.pixmap->setDevicePixelRatio(pixmap->devicePixelRatioF()); tile.rotation = rotation; totalPixels += tile.pixmap->width()*tile.pixmap->height(); tile.dirty = false; @@ -421,7 +431,8 @@ h = tile.pixmap->width(); break; } - QPixmap *rotatedPixmap = new QPixmap( w, h ); + QPixmap *rotatedPixmap = new QPixmap( w * tile.pixmap->width(), h * tile.pixmap->height() ); + rotatedPixmap->setDevicePixelRatio(tile.pixmap->devicePixelRatioF()); QPainter p( rotatedPixmap ); p.rotate( angleToRotate ); p.translate( xOffset, yOffset ); diff --git a/core/tilesmanager_p.h b/core/tilesmanager_p.h --- a/core/tilesmanager_p.h +++ b/core/tilesmanager_p.h @@ -104,7 +104,7 @@ PixmapTile ///< Return only tiles with pixmap }; - TilesManager( int pageNumber, int width, int height, Rotation rotation = Rotation0 ); + TilesManager( int pageNumber, int width, int height, qreal dpr, Rotation rotation = Rotation0 ); ~TilesManager(); /** @@ -176,6 +176,11 @@ int height() const; /** + * Gets the device pixel ratio of the page in tiles manager + */ + qreal devicePixelRatio() const; + + /** * Inform the new rotation of the page */ void setRotation( Rotation rotation ); diff --git a/generators/kimgio/tests/kimgiotest.cpp b/generators/kimgio/tests/kimgiotest.cpp --- a/generators/kimgio/tests/kimgiotest.cpp +++ b/generators/kimgio/tests/kimgiotest.cpp @@ -97,7 +97,7 @@ QCOMPARE( m_document->page(0)->height(), double(2) ); // Generate pixmap - Okular::PixmapRequest *req = new Okular::PixmapRequest( dummyDocumentObserver, 0, 3, 2, + Okular::PixmapRequest *req = new Okular::PixmapRequest( dummyDocumentObserver, 0, 3, 2, 1.0, 1, Okular::PixmapRequest::NoFeature ); m_document->requestPixmaps( QLinkedList() << req ); QVERIFY( m_document->page(0)->hasPixmap( dummyDocumentObserver, 3, 2 ) ); diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -627,6 +627,7 @@ const QSizeF pSize = p->pageSizeF(); w = pSize.width() / 72.0 * dpi().width(); h = pSize.height() / 72.0 * dpi().height(); + Okular::Rotation orientation = Okular::Rotation0; switch (p->orientation()) { diff --git a/mobile/components/pageitem.cpp b/mobile/components/pageitem.cpp --- a/mobile/components/pageitem.cpp +++ b/mobile/components/pageitem.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -312,7 +313,7 @@ if (m_intentionalDraw) { QLinkedList requestedPixmaps; - requestedPixmaps.push_back(new Okular::PixmapRequest(observer, m_viewPort.pageNumber, width(), height(), priority, Okular::PixmapRequest::Asynchronous)); + requestedPixmaps.push_back(new Okular::PixmapRequest(observer, m_viewPort.pageNumber, width(), height(), qApp->devicePixelRatio(), priority, Okular::PixmapRequest::Asynchronous)); const Okular::Document::PixmapRequestFlag prf = m_isThumbnail ? Okular::Document::NoOption : Okular::Document::RemoveAllPrevious; m_documentItem.data()->document()->requestPixmaps(requestedPixmaps, prf); m_intentionalDraw = false; diff --git a/shell/main.cpp b/shell/main.cpp --- a/shell/main.cpp +++ b/shell/main.cpp @@ -29,12 +29,12 @@ int main(int argc, char** argv) { - QApplication app(argc, argv); + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication app(argc, argv); KLocalizedString::setApplicationDomain("okular"); KAboutData aboutData = okularAboutData(); - app.setApplicationName(aboutData.applicationData().componentName()); app.setApplicationDisplayName(aboutData.applicationData().displayName()); app.setApplicationVersion(aboutData.version()); diff --git a/ui/magnifierview.cpp b/ui/magnifierview.cpp --- a/ui/magnifierview.cpp +++ b/ui/magnifierview.cpp @@ -130,7 +130,7 @@ { QLinkedList< Okular::PixmapRequest * > requestedPixmaps; - Okular::PixmapRequest *p = new Okular::PixmapRequest( this, m_current, full_width, full_height, PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous ); + Okular::PixmapRequest *p = new Okular::PixmapRequest( this, m_current, full_width, full_height, devicePixelRatioF(), PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous ); if ( m_page->hasTilesManager( this ) ) { p->setTile( true ); diff --git a/ui/pagepainter.h b/ui/pagepainter.h --- a/ui/pagepainter.h +++ b/ui/pagepainter.h @@ -56,12 +56,6 @@ static void cropPixmapOnImage( QImage & dest, const QPixmap * src, const QRect & r ); static void recolor(QImage *image, const QColor &foreground, const QColor &background); - // create an image taking the 'cropRect' portion of an image scaled - // to 'scaledWidth' by 'scaledHeight' pixels. cropRect must be inside - // the QRect(0,0, scaledWidth,scaledHeight) - static void scalePixmapOnImage( QImage & dest, const QPixmap *src, - int scaledWidth, int scaledHeight, const QRect & cropRect, QImage::Format format = QImage::Format_ARGB32_Premultiplied ); - // set the alpha component of the image to a given value static void changeImageAlpha( QImage & image, unsigned int alpha ); diff --git a/ui/pagepainter.cpp b/ui/pagepainter.cpp --- a/ui/pagepainter.cpp +++ b/ui/pagepainter.cpp @@ -18,6 +18,7 @@ #include #include #include +#include // system includes #include @@ -35,7 +36,7 @@ #include "settings_core.h" #include "ui/debug_ui.h" -Q_GLOBAL_STATIC_WITH_ARGS( QPixmap, busyPixmap, ( KIconLoader::global()->loadIcon(QLatin1String("okular"), KIconLoader::NoGroup, 32, KIconLoader::DefaultState, QStringList(), 0, true) ) ) +Q_GLOBAL_STATIC_WITH_ARGS( QPixmap, busyPixmap, ( KIconLoader::global()->loadIcon(QLatin1String("okular"), KIconLoader::NoGroup, IconSize(KIconLoader::Desktop), KIconLoader::DefaultState, QStringList(), 0, true) ) ) #define TEXTANNOTATION_ICONSIZE 24 @@ -62,11 +63,19 @@ Okular::DocumentObserver *observer, int flags, int scaledWidth, int scaledHeight, const QRect &limits, const Okular::NormalizedRect &crop, Okular::NormalizedPoint *viewPortPoint ) { + qreal dpr = destPainter->device()->devicePixelRatioF(); + /* Calculate the cropped geometry of the page */ QRect scaledCrop = crop.geometry( scaledWidth, scaledHeight ); + const QRect dScaledCrop(QRectF(scaledCrop.x() * dpr, scaledCrop.y() * dpr, scaledCrop.width() * dpr, scaledCrop.height() * dpr).toAlignedRect()); + int croppedWidth = scaledCrop.width(); int croppedHeight = scaledCrop.height(); + int dScaledWidth = floor(scaledWidth * dpr) + 1; + int dScaledHeight = floor(scaledHeight * dpr) + 1; + const QRect dLimits(QRectF(limits.x() * dpr, limits.y() * dpr, limits.width() * dpr, limits.height() * dpr).toAlignedRect()); + QColor paperColor = Qt::white; QColor backgroundColor = paperColor; if ( Okular::SettingsCore::changeColors() ) @@ -94,17 +103,18 @@ if ( !hasTilesManager ) { /** 1 - RETRIEVE THE 'PAGE+ID' PIXMAP OR A SIMILAR 'PAGE' ONE **/ - pixmap = page->_o_nearestPixmap( observer, scaledWidth, scaledHeight ); + pixmap = page->_o_nearestPixmap( observer, scaledWidth, scaledHeight, dpr ); /** 1B - IF NO PIXMAP, DRAW EMPTY PAGE **/ - double pixmapRescaleRatio = pixmap ? scaledWidth / (double)pixmap->width() : -1; + double pixmapRescaleRatio = pixmap ? dScaledWidth / (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 > 60000000L) ) + (dScaledWidth > pixmap->width() && pixmapPixels > 60000000L) ) { // draw something on the blank page: the okular icon or a cross (as a fallback) if ( !busyPixmap()->isNull() ) { + busyPixmap->setDevicePixelRatio(dpr); destPainter->drawPixmap( QPoint( 10, 10 ), *busyPixmap() ); } else @@ -135,10 +145,10 @@ if ( canDrawHighlights || canDrawTextSelection || canDrawAnnotations ) { // precalc normalized 'limits rect' for intersection - double nXMin = ( (double)limits.left() / (double)scaledWidth ) + crop.left, - nXMax = ( (double)limits.right() / (double)scaledWidth ) + crop.left, - nYMin = ( (double)limits.top() / (double)scaledHeight ) + crop.top, - nYMax = ( (double)limits.bottom() / (double)scaledHeight ) + crop.top; + double nXMin = ( (double)limits.left() / dScaledWidth ) + crop.left, + nXMax = ( (double)limits.right() / dScaledWidth ) + crop.left, + nYMin = ( (double)limits.top() / dScaledHeight ) + crop.top, + nYMax = ( (double)limits.bottom() / dScaledHeight ) + crop.top; // append all highlights inside limits to their list if ( canDrawHighlights ) { @@ -240,9 +250,12 @@ QPixmap * backPixmap = 0; QPainter * mixedPainter = 0; QRect limitsInPixmap = limits.translated( scaledCrop.topLeft() ); + QRect dLimitsInPixmap = dLimits.translated( dScaledCrop.topLeft() ); + // limits within full (scaled but uncropped) pixmap /** 4A -- REGULAR FLOW. PAINT PIXMAP NORMAL OR RESCALED USING GIVEN QPAINTER **/ + /* when zoomed in */ if ( !useBackBuffer ) { if ( hasTilesManager ) @@ -254,32 +267,26 @@ { const Okular::Tile &tile = *tIt; QRect tileRect = tile.rect().geometry( scaledWidth, scaledHeight ).translated( -scaledCrop.topLeft() ); + QRect dTileRect = QRectF(tileRect.x() * dpr, tileRect.y() * dpr, tileRect.width() * dpr, tileRect.height() * dpr).toAlignedRect(); QRect limitsInTile = limits & tileRect; + QRectF dLimitsInTile = dLimits & dTileRect; + if ( !limitsInTile.isEmpty() ) { - if ( tile.pixmap()->width() == tileRect.width() && tile.pixmap()->height() == tileRect.height() ) + if ( tile.pixmap()->width() == (dTileRect.width()) && tile.pixmap()->height() == (dTileRect.height()) ) { destPainter->drawPixmap( limitsInTile.topLeft(), *(tile.pixmap()), - limitsInTile.translated( -tileRect.topLeft() ) ); - else - destPainter->drawPixmap( tileRect, *(tile.pixmap()) ); + dLimitsInTile.translated( -dTileRect.topLeft() ) ); + } else { + destPainter->drawPixmap( tileRect, *(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() ); - } + QPixmap scaledCroppedPixmap = pixmap->scaled(dScaledWidth, dScaledHeight).copy(dLimitsInPixmap); + destPainter->drawPixmap( limits.topLeft(), scaledCroppedPixmap, QRectF(0, 0, dLimits.width(),dLimits.height())); } // 4A.2. active painter is the one passed to this method @@ -289,7 +296,10 @@ else { // the image over which we are going to draw - QImage backImage; + QImage backImage = QImage( dLimits.width(), dLimits.height(), QImage::Format_ARGB32_Premultiplied ); + backImage.setDevicePixelRatio(dpr); + backImage.fill( paperColor ); + QPainter p( &backImage ); bool has_alpha; if ( pixmap ) @@ -299,9 +309,6 @@ 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 tiles = page->tilesAt( observer, normalizedLimits ); QList::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd(); @@ -309,21 +316,22 @@ { const Okular::Tile &tile = *tIt; QRect tileRect = tile.rect().geometry( scaledWidth, scaledHeight ).translated( -scaledCrop.topLeft() ); + QRectF dTileRect(tileRect.x() * dpr, tileRect.y() * dpr, tileRect.width() * dpr, tileRect.height() * dpr); 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() ) + if ( tile.pixmap()->width() == dTileRect.width() && tile.pixmap()->height() == dTileRect.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(); + double xScale = tile.pixmap()->width() / dTileRect.width(); + double yScale = tile.pixmap()->height() / dTileRect.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() ) ); @@ -331,17 +339,17 @@ } ++tIt; } - p.end(); } 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 ); + QPixmap scaledCroppedPixmap = pixmap->scaled(dScaledWidth, dScaledHeight).copy(dLimitsInPixmap); + scaledCroppedPixmap.setDevicePixelRatio(dpr); + p.drawPixmap( limits.topLeft(), scaledCroppedPixmap, QRect(0, 0, dLimitsInPixmap.width(),dLimitsInPixmap.height())); } + p.end(); + // 4B.2. modify pixmap following accessibility settings if ( bufferAccessibility ) { @@ -379,6 +387,7 @@ break; } } + // 4B.3. highlight rects in page if ( bufferedHighlights ) { @@ -388,10 +397,20 @@ { const Okular::NormalizedRect & r = (*hIt).second; // find out the rect to highlight on pixmap + QRect highlightRect = r.geometry( scaledWidth, scaledHeight ).translated( -scaledCrop.topLeft() ).intersected( limits ); highlightRect.translate( -limits.left(), -limits.top() ); + QPainter painter(&backImage); + painter.fillRect(highlightRect, QColor((*hIt).first.red(), (*hIt).first.green(), (*hIt).first.blue(), 150)); + + // the code below works too, but the mordern method would be to just use a QPainter? + // highlight composition (product: highlight color * destcolor) + /* + QRect highlightRect = r.geometry( dScaledWidth, dScaledHeight ).translated( -dScaledCrop.topLeft() ).intersected( dLimits ); + highlightRect.translate( -dLimits.left(), -dLimits.top() ); + unsigned int * data = (unsigned int *)backImage.bits(); int val, newR, newG, newB, rh = (*hIt).first.red(), @@ -427,8 +446,10 @@ } offset += backImage.width(); } + }*/ } } + // 4B.4. paint annotations [COMPOSITED ONES] if ( bufferedAnnotations ) { @@ -621,7 +642,6 @@ } } // end current annotation drawing } - if(viewPortPoint) { QPainter painter(&backImage); @@ -651,6 +671,7 @@ // 4B.5. create the back pixmap converting from the local image backPixmap = new QPixmap( QPixmap::fromImage( backImage ) ); + backPixmap->setDevicePixelRatio(dpr); // 4B.6. create a painter over the pixmap and set it as the active one mixedPainter = new QPainter( backPixmap ); @@ -683,6 +704,7 @@ QRect annotRect = annotBoundary.intersected( limits ); QRect innerRect( annotRect.left() - annotBoundary.left(), annotRect.top() - annotBoundary.top(), annotRect.width(), annotRect.height() ); + QRectF dInnerRect(innerRect.x() * dpr, innerRect.y() * dpr, innerRect.width() * dpr, innerRect.height() * dpr); Okular::Annotation::SubType type = a->subType(); @@ -726,23 +748,24 @@ QPixmap pixmap = GuiUtils::iconLoader()->loadIcon( text->textIcon().toLower(), KIconLoader::User, 32, KIconLoader::DefaultState, QStringList(), &path, true ); if ( path.isEmpty() ) pixmap = GuiUtils::iconLoader()->loadIcon( text->textIcon().toLower(), KIconLoader::NoGroup, 32 ); - QImage scaledImage; - QRect annotBoundary2 = QRect( annotBoundary.topLeft(), QSize( TEXTANNOTATION_ICONSIZE, TEXTANNOTATION_ICONSIZE ) ); + QRect annotBoundary2 = QRect( annotBoundary.topLeft(), QSize( TEXTANNOTATION_ICONSIZE * dpr, TEXTANNOTATION_ICONSIZE * dpr ) ); QRect annotRect2 = annotBoundary2.intersected( limits ); QRect innerRect2( annotRect2.left() - annotBoundary2.left(), annotRect2.top() - annotBoundary2.top(), annotRect2.width(), annotRect2.height() ); - scalePixmapOnImage( scaledImage, &pixmap, - TEXTANNOTATION_ICONSIZE, TEXTANNOTATION_ICONSIZE, - innerRect2, QImage::Format_ARGB32 ); + + QPixmap scaledCroppedPixmap = pixmap.scaled(TEXTANNOTATION_ICONSIZE * dpr, TEXTANNOTATION_ICONSIZE * dpr).copy(dInnerRect.toAlignedRect()); + scaledCroppedPixmap.setDevicePixelRatio(dpr); + QImage scaledCroppedImage = scaledCroppedPixmap.toImage(); + // if the annotation color is valid (ie it was set), then // use it to colorize the icon, otherwise the icon will be // "gray" if ( a->style().color().isValid() ) - GuiUtils::colorizeImage( scaledImage, a->style().color(), opacity ); - pixmap = QPixmap::fromImage( scaledImage ); + GuiUtils::colorizeImage( scaledCroppedImage, a->style().color(), opacity ); + pixmap = QPixmap::fromImage( scaledCroppedImage ); - // draw the mangled image to painter - mixedPainter->drawPixmap( annotRect.topLeft(), pixmap ); + // draw the mangled image to painter + mixedPainter->drawPixmap( annotRect.topLeft(), pixmap); } } @@ -755,12 +778,16 @@ QPixmap pixmap = GuiUtils::loadStamp( stamp->stampIconName(), annotBoundary.size() ); if ( !pixmap.isNull() ) // should never happen but can happen on huge sizes { - QImage scaledImage; - scalePixmapOnImage( scaledImage, &pixmap, annotBoundary.width(), - annotBoundary.height(), innerRect, QImage::Format_ARGB32 ); + const QRect dInnerRect(QRectF(innerRect.x() * dpr, innerRect.y() * dpr, innerRect.width() * dpr, innerRect.height() * dpr).toAlignedRect()); + + QPixmap scaledCroppedPixmap = pixmap.scaled(annotBoundary.width() * dpr, annotBoundary.height() * dpr).copy(dInnerRect); + scaledCroppedPixmap.setDevicePixelRatio(dpr); + + QImage scaledCroppedImage = scaledCroppedPixmap.toImage(); + if ( opacity < 255 ) - changeImageAlpha( scaledImage, opacity ); - pixmap = QPixmap::fromImage( scaledImage ); + changeImageAlpha( scaledCroppedImage, opacity ); + pixmap = QPixmap::fromImage( scaledCroppedImage ); // draw the scaled and al mixedPainter->drawPixmap( annotRect.topLeft(), pixmap ); @@ -865,8 +892,10 @@ /** Private Helpers :: Pixmap conversion **/ void PagePainter::cropPixmapOnImage( QImage & dest, const QPixmap * src, const QRect & r ) { + qreal dpr = src->devicePixelRatioF(); + // handle quickly the case in which the whole pixmap has to be converted - if ( r == QRect( 0, 0, src->width(), src->height() ) ) + if ( r == QRect( 0, 0, src->width() / dpr, src->height() / dpr ) ) { dest = src->toImage(); dest = dest.convertToFormat(QImage::Format_ARGB32_Premultiplied); @@ -874,7 +903,8 @@ // else copy a portion of the src to an internal pixmap (smaller) and convert it else { - QImage croppedImage( r.width(), r.height(), QImage::Format_ARGB32_Premultiplied ); + QImage croppedImage( r.width() * dpr, r.height() * dpr, QImage::Format_ARGB32_Premultiplied ); + croppedImage.setDevicePixelRatio(dpr); QPainter p( &croppedImage ); p.drawPixmap( 0, 0, *src, r.left(), r.top(), r.width(), r.height() ); p.end(); @@ -908,40 +938,6 @@ } } -void PagePainter::scalePixmapOnImage ( QImage & dest, const QPixmap * src, - int scaledWidth, int scaledHeight, const QRect & cropRect, QImage::Format format ) -{ - // {source, destination, scaling} params - int srcWidth = src->width(), - srcHeight = src->height(), - destLeft = cropRect.left(), - destTop = cropRect.top(), - destWidth = cropRect.width(), - destHeight = cropRect.height(); - - // destination image (same geometry as the pageLimits rect) - dest = QImage( destWidth, destHeight, format ); - unsigned int * destData = (unsigned int *)dest.bits(); - - // source image (1:1 conversion from pixmap) - QImage srcImage = src->toImage().convertToFormat(format); - unsigned int * srcData = (unsigned int *)srcImage.bits(); - - // precalc the x correspondancy conversion in a lookup table - QVarLengthArray xOffset( destWidth ); - for ( int x = 0; x < destWidth; x++ ) - xOffset[ x ] = ((x + destLeft) * srcWidth) / scaledWidth; - - // for each pixel of the destination image apply the color of the - // corresponsing pixel on the source image (note: keep parenthesis) - for ( int y = 0; y < destHeight; y++ ) - { - unsigned int srcOffset = srcWidth * (((destTop + y) * srcHeight) / scaledHeight); - for ( int x = 0; x < destWidth; x++ ) - (*destData++) = srcData[ srcOffset + xOffset[x] ]; - } -} - /** Private Helpers :: Image Drawing **/ // from Arthur - qt4 static inline int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; } diff --git a/ui/pageview.h b/ui/pageview.h --- a/ui/pageview.h +++ b/ui/pageview.h @@ -36,6 +36,7 @@ class Annotation; class MovieAction; class RenditionAction; +class PixmapRequest; } class FormWidgetIface; @@ -261,6 +262,7 @@ void slotProcessRenditionAction( const Okular::RenditionAction *action ); void slotToggleChangeColors(); void slotFitWindowToPage(); + void slotRequestPreloadPixmap( Okular::DocumentObserver *observer, const PageViewItem *i, const QRect &expandedViewportRect, QLinkedList< Okular::PixmapRequest * > *requestedPixmaps ); }; #endif diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -976,7 +976,7 @@ QVector< Okular::Page * >::const_iterator setIt = pageSet.constBegin(), setEnd = pageSet.constEnd(); for ( ; setIt != setEnd; ++setIt ) { - PageViewItem * item = new PageViewItem( *setIt ); + PageViewItem * item = new PageViewItem( *setIt, devicePixelRatio() ); d->items.push_back( item ); #ifdef PAGEVIEW_DEBUG qCDebug(OkularUiDebug).nospace() << "cropped geom for " << d->items.last()->pageNumber() << " is " << d->items.last()->croppedGeometry(); @@ -1646,8 +1646,10 @@ if ( wantCompositing && Okular::Settings::enableCompositing() ) { // create pixmap and open a painter over it (contents{left,top} becomes pixmap {0,0}) - QPixmap doubleBuffer( contentsRect.size() ); + QPixmap doubleBuffer( contentsRect.size() * devicePixelRatioF() ); + doubleBuffer.setDevicePixelRatio(devicePixelRatioF()); QPainter pixmapPainter( &doubleBuffer ); + pixmapPainter.translate( -contentsRect.left(), -contentsRect.top() ); // 1) Layer 0: paint items and clear bg on unpainted rects @@ -1661,11 +1663,12 @@ if ( blendRect.isValid() ) { // grab current pixmap into a new one to colorize contents - QPixmap blendedPixmap( blendRect.width(), blendRect.height() ); + QPixmap blendedPixmap( blendRect.width() * devicePixelRatioF(), blendRect.height() * devicePixelRatioF() ); + blendedPixmap.setDevicePixelRatio(devicePixelRatioF()); QPainter p( &blendedPixmap ); p.drawPixmap( 0, 0, doubleBuffer, blendRect.left() - contentsRect.left(), blendRect.top() - contentsRect.top(), - blendRect.width(), blendRect.height() ); + blendRect.width() * devicePixelRatioF(), blendRect.height() * devicePixelRatioF() ); QColor blCol = selBlendColor.dark( 140 ); blCol.setAlphaF( 0.2 ); @@ -1692,11 +1695,12 @@ if ( blendRect.isValid() ) { // grab current pixmap into a new one to colorize contents - QPixmap blendedPixmap( blendRect.width(), blendRect.height() ); + QPixmap blendedPixmap( blendRect.width() * devicePixelRatioF(), blendRect.height() * devicePixelRatioF() ); + blendedPixmap.setDevicePixelRatio(devicePixelRatioF()); QPainter p( &blendedPixmap ); p.drawPixmap( 0, 0, doubleBuffer, blendRect.left() - contentsRect.left(), blendRect.top() - contentsRect.top(), - blendRect.width(), blendRect.height() ); + blendRect.width() * devicePixelRatioF(), blendRect.height() * devicePixelRatioF() ); QColor blCol = d->mouseSelectionColor.dark( 140 ); blCol.setAlphaF( 0.2 ); @@ -4480,7 +4484,7 @@ slotRequestVisiblePixmaps(); } -static void slotRequestPreloadPixmap( Okular::DocumentObserver * observer, const PageViewItem * i, const QRect &expandedViewportRect, QLinkedList< Okular::PixmapRequest * > *requestedPixmaps ) +void PageView::slotRequestPreloadPixmap( Okular::DocumentObserver * observer, const PageViewItem * i, const QRect &expandedViewportRect, QLinkedList< Okular::PixmapRequest * > *requestedPixmaps ) { Okular::NormalizedRect preRenderRegion; const QRect intersectionRect = expandedViewportRect.intersected( i->croppedGeometry() ); @@ -4495,7 +4499,7 @@ 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 ); + Okular::PixmapRequest * p = new Okular::PixmapRequest( observer, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), devicePixelRatioF(), PAGEVIEW_PRELOAD_PRIO, requestFeatures ); requestedPixmaps->push_back( p ); p->setNormalizedRect( preRenderRegion ); @@ -4503,7 +4507,7 @@ } else if ( !pageHasTilesManager ) { - Okular::PixmapRequest * p = new Okular::PixmapRequest( observer, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRELOAD_PRIO, requestFeatures ); + Okular::PixmapRequest * p = new Okular::PixmapRequest( observer, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), devicePixelRatioF(), PAGEVIEW_PRELOAD_PRIO, requestFeatures ); requestedPixmaps->push_back( p ); p->setNormalizedRect( preRenderRegion ); } @@ -4599,7 +4603,7 @@ #ifdef PAGEVIEW_DEBUG kWarning() << "rerequesting visible pixmaps for page" << i->pageNumber() << "!"; #endif - Okular::PixmapRequest * p = new Okular::PixmapRequest( this, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous ); + Okular::PixmapRequest * p = new Okular::PixmapRequest( this, i->pageNumber(), i->uncroppedWidth(), i->uncroppedHeight(), devicePixelRatioF(), PAGEVIEW_PRIO, Okular::PixmapRequest::Asynchronous ); requestedPixmaps.push_back( p ); if ( i->page()->hasTilesManager( this ) ) @@ -5317,17 +5321,17 @@ void PageView::slotFitWindowToPage() { - PageViewItem currentPageItem = NULL; + const PageViewItem *currentPageItem; QSize viewportSize = viewport()->size(); foreach ( const PageViewItem * pageItem, d->items ) { if ( pageItem->isVisible() ) { - currentPageItem = *pageItem; + currentPageItem = pageItem; break; } } - const QSize pageSize = QSize( currentPageItem.uncroppedWidth() + kcolWidthMargin, currentPageItem.uncroppedHeight() + krowHeightMargin ); + const QSize pageSize = QSize( currentPageItem->uncroppedWidth() + kcolWidthMargin, currentPageItem->uncroppedHeight() + krowHeightMargin ); if ( verticalScrollBar()->isVisible() ) viewportSize.setWidth( viewportSize.width() + verticalScrollBar()->width() ); if ( horizontalScrollBar()->isVisible() ) diff --git a/ui/pageviewutils.h b/ui/pageviewutils.h --- a/ui/pageviewutils.h +++ b/ui/pageviewutils.h @@ -40,7 +40,7 @@ class PageViewItem { public: - PageViewItem( const Okular::Page * page ); + PageViewItem( const Okular::Page * page, qreal dpr ); ~PageViewItem(); const Okular::Page * page() const; @@ -81,6 +81,7 @@ private: const Okular::Page * m_page; + qreal m_dpr; double m_zoomFactor; bool m_visible; bool m_formsVisible; diff --git a/ui/pageviewutils.cpp b/ui/pageviewutils.cpp --- a/ui/pageviewutils.cpp +++ b/ui/pageviewutils.cpp @@ -43,8 +43,8 @@ /** PageViewItem */ /*********************/ -PageViewItem::PageViewItem( const Okular::Page * page ) - : m_page( page ), m_zoomFactor( 1.0 ), m_visible( true ), +PageViewItem::PageViewItem( const Okular::Page * page, qreal dpr ) + : m_page( page ), m_dpr( dpr ), m_zoomFactor( 1.0 ), m_visible( true ), m_formsVisible( false ), m_crop( 0., 0., 1., 1. ) { } diff --git a/ui/presentationwidget.cpp b/ui/presentationwidget.cpp --- a/ui/presentationwidget.cpp +++ b/ui/presentationwidget.cpp @@ -1384,7 +1384,7 @@ QApplication::setOverrideCursor( QCursor( Qt::BusyCursor ) ); // request the pixmap QLinkedList< Okular::PixmapRequest * > requests; - requests.push_back( new Okular::PixmapRequest( this, m_frameIndex, pixW, pixH, PRESENTATION_PRIO, Okular::PixmapRequest::NoFeature ) ); + requests.push_back( new Okular::PixmapRequest( this, m_frameIndex, pixW, pixH, devicePixelRatioF(), PRESENTATION_PRIO, Okular::PixmapRequest::NoFeature ) ); // restore cursor QApplication::restoreOverrideCursor(); // ask for next and previous page if not in low memory usage setting @@ -1408,7 +1408,7 @@ pixW = nextFrame->geometry.width(); pixH = nextFrame->geometry.height(); if ( !nextFrame->page->hasPixmap( this, pixW, pixH ) ) - requests.push_back( new Okular::PixmapRequest( this, tailRequest, pixW, pixH, PRESENTATION_PRELOAD_PRIO, requestFeatures ) ); + requests.push_back( new Okular::PixmapRequest( this, tailRequest, pixW, pixH, devicePixelRatioF(), PRESENTATION_PRELOAD_PRIO, requestFeatures ) ); } int headRequest = m_frameIndex - j; @@ -1418,7 +1418,7 @@ pixW = prevFrame->geometry.width(); pixH = prevFrame->geometry.height(); if ( !prevFrame->page->hasPixmap( this, pixW, pixH ) ) - requests.push_back( new Okular::PixmapRequest( this, headRequest, pixW, pixH, PRESENTATION_PRELOAD_PRIO, requestFeatures ) ); + requests.push_back( new Okular::PixmapRequest( this, headRequest, pixW, pixH, devicePixelRatioF(), PRESENTATION_PRELOAD_PRIO, requestFeatures ) ); } // stop if we've already reached both ends of the document diff --git a/ui/thumbnaillist.cpp b/ui/thumbnaillist.cpp --- a/ui/thumbnaillist.cpp +++ b/ui/thumbnaillist.cpp @@ -624,7 +624,7 @@ // if pixmap not present add it to requests if ( !t->page()->hasPixmap( q, t->pixmapWidth(), t->pixmapHeight() ) ) { - Okular::PixmapRequest * p = new Okular::PixmapRequest( q, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight(), THUMBNAILS_PRIO, Okular::PixmapRequest::Asynchronous ); + Okular::PixmapRequest * p = new Okular::PixmapRequest( q, t->pageNumber(), t->pixmapWidth(), t->pixmapHeight(), devicePixelRatioF(), THUMBNAILS_PRIO, Okular::PixmapRequest::Asynchronous ); requestedPixmaps.push_back( p ); } }