HiDPI Support for Okular

Summary:
This patch enables HiDPI throughout the application

Every pixmap is multiplied by the devicePixelRatioF
QPainter code is ajusted to take the DPR value into account

All pixmaps get cached with the highest DPR of all screens. When moving the application to another screen, the cache doesn't have to be invalidated.

BUGS: 362856 383589
REVIEW: D6268
This commit is contained in:
Lukas Hetzenecker 2017-10-14 14:47:20 +02:00
parent 6d7403ff13
commit ecc1141e02
16 changed files with 168 additions and 131 deletions

View file

@ -26,7 +26,7 @@ WidgetConfigurationToolsBase::WidgetConfigurationToolsBase( QWidget * parent )
{
QHBoxLayout *hBoxLayout = new QHBoxLayout( this );
m_list = new QListWidget( this );
m_list->setIconSize( QSize( 64, 64 ) );
m_list->setIconSize( QSize( 32, 32 ) );
hBoxLayout->addWidget( m_list );
QVBoxLayout *vBoxLayout = new QVBoxLayout();

View file

@ -1525,7 +1525,7 @@ void DocumentPrivate::refreshPixmaps( int pageNumber )
for ( ; it != itEnd; ++it )
{
QSize size = (*it).m_pixmap->size();
PixmapRequest * p = new PixmapRequest( it.key(), pageNumber, size.width(), size.height(), 1, PixmapRequest::Asynchronous );
PixmapRequest * p = new PixmapRequest( it.key(), pageNumber, size.width() / qApp->devicePixelRatio(), size.height() / qApp->devicePixelRatio(), 1, PixmapRequest::Asynchronous );
p->d->mForce = true;
requestedPixmaps.push_back( p );
}
@ -1537,7 +1537,7 @@ void DocumentPrivate::refreshPixmaps( int pageNumber )
{
tilesManager->markDirty();
PixmapRequest * p = new PixmapRequest( observer, pageNumber, tilesManager->width(), tilesManager->height(), 1, PixmapRequest::Asynchronous );
PixmapRequest * p = new PixmapRequest( observer, pageNumber, tilesManager->width() / qApp->devicePixelRatio(), tilesManager->height() / qApp->devicePixelRatio(), 1, PixmapRequest::Asynchronous );
NormalizedRect tilesRect;

View file

@ -12,6 +12,7 @@
#include "generator_p.h"
#include "observer.h"
#include <QApplication>
#include <qeventloop.h>
#include <QtPrintSupport/QPrinter>
@ -490,8 +491,8 @@ PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int wi
{
d->mObserver = observer;
d->mPageNumber = pageNumber;
d->mWidth = width;
d->mHeight = height;
d->mWidth = ceil(width * qApp->devicePixelRatio());
d->mHeight = ceil(height * qApp->devicePixelRatio());
d->mPriority = priority;
d->mFeatures = features;
d->mForce = false;

View file

@ -29,8 +29,9 @@
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();

View file

@ -10,12 +10,17 @@ install(FILES
# install annotation tool images
install(FILES
tool-base-okular.png
tool-base-okular@2x.png
tool-highlighter-okular-colorizable.png
tool-highlighter-okular-colorizable@2x.png
tool-ink-okular-colorizable.png
tool-ink-okular-colorizable@2x.png
tool-note.png
tool-note-okular-colorizable.png
tool-note-okular-colorizable@2x.png
tool-note-inline.png
tool-note-inline-okular-colorizable.png
tool-note-inline-okular-colorizable@2x.png
DESTINATION ${KDE_INSTALL_DATADIR}/okular/pics)
# install annotation page images
install(FILES

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -18,6 +18,7 @@
#include <kiconloader.h>
#include <QtCore/QDebug>
#include <QApplication>
#include <QIcon>
// system includes
#include <math.h>
@ -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,22 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
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 );
/* variables prefixed with d are in the device pixels coordinate system, which translates to the rendered output - that means,
* multiplied with the device pixel ratio of the target PaintDevice */
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 = ceil(scaledWidth * dpr);
int dScaledHeight = ceil(scaledHeight * dpr);
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() )
@ -89,22 +101,28 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
destPainter->fillRect( limits, backgroundColor );
const bool hasTilesManager = page->hasTilesManager( observer );
const QPixmap *pixmap = nullptr;
QPixmap pixmap;
if ( !hasTilesManager )
{
/** 1 - RETRIEVE THE 'PAGE+ID' PIXMAP OR A SIMILAR 'PAGE' ONE **/
pixmap = page->_o_nearestPixmap( observer, scaledWidth, scaledHeight );
const QPixmap *p = page->_o_nearestPixmap( observer, dScaledWidth, dScaledHeight );
if (p != NULL) {
pixmap = *p;
pixmap.setDevicePixelRatio( qApp->devicePixelRatio() );
}
/** 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 > 60000000L) )
double pixmapRescaleRatio = !pixmap.isNull() ? dScaledWidth / (double)pixmap.width() : -1;
long pixmapPixels = !pixmap.isNull() ? (long)pixmap.width() * (long)pixmap.height() : 0;
if ( pixmap.isNull() || pixmapRescaleRatio > 20.0 || pixmapRescaleRatio < 0.25 ||
(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 +153,10 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
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,6 +258,8 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
QPixmap * backPixmap = nullptr;
QPainter * mixedPainter = nullptr;
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 **/
@ -254,32 +274,30 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
{
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() )
destPainter->drawPixmap( limitsInTile.topLeft(), *(tile.pixmap()),
limitsInTile.translated( -tileRect.topLeft() ) );
else
destPainter->drawPixmap( tileRect, *(tile.pixmap()) );
QPixmap* tilePixmap = tile.pixmap();
tilePixmap->setDevicePixelRatio( qApp->devicePixelRatio() );
if ( tilePixmap->width() == dTileRect.width() && tilePixmap->height() == dTileRect.height() ) {
destPainter->drawPixmap( limitsInTile.topLeft(), *tilePixmap,
dLimitsInTile.translated( -dTileRect.topLeft() ) );
} else {
destPainter->drawPixmap( tileRect, *tilePixmap );
}
}
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);
scaledCroppedPixmap.setDevicePixelRatio(dpr);
destPainter->drawPixmap( limits.topLeft(), scaledCroppedPixmap, QRectF(0, 0, dLimits.width(),dLimits.height()));
}
// 4A.2. active painter is the one passed to this method
@ -289,13 +307,13 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
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 );
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();
@ -303,36 +321,42 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
{
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;
QRect dLimitsInTile = dLimits & dTileRect;
if ( !limitsInTile.isEmpty() )
{
if ( tile.pixmap()->width() == tileRect.width() && tile.pixmap()->height() == tileRect.height() )
QPixmap* tilePixmap = tile.pixmap();
tilePixmap->setDevicePixelRatio( qApp->devicePixelRatio() );
if ( tilePixmap->width() == dTileRect.width() && tilePixmap->height() == dTileRect.height() )
{
p.drawPixmap( limitsInTile.translated( -limits.topLeft() ).topLeft(), *(tile.pixmap()),
limitsInTile.translated( -tileRect.topLeft() ) );
p.drawPixmap( limitsInTile.translated( -limits.topLeft() ).topLeft(), *tilePixmap,
dLimitsInTile.translated( -dTileRect.topLeft() ) );
}
else
{
double xScale = tile.pixmap()->width() / (double)tileRect.width();
double yScale = tile.pixmap()->height() / (double)tileRect.height();
double xScale = tilePixmap->width() / (double)dTileRect.width();
double yScale = tilePixmap->height() / (double)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() ) );
p.drawPixmap( limitsInTile.translated( -limits.topLeft() ), *tilePixmap,
transform.mapRect( dLimitsInTile ).translated( -transform.mapRect( dTileRect ).topLeft() ) );
}
}
++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( 0, 0, scaledCroppedPixmap );
}
p.end();
// 4B.2. modify pixmap following accessibility settings
if ( bufferAccessibility )
{
@ -370,6 +394,7 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
break;
}
}
// 4B.3. highlight rects in page
if ( bufferedHighlights )
{
@ -387,6 +412,7 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
painter.fillRect(highlightRect, highlightColor);
}
}
// 4B.4. paint annotations [COMPOSITED ONES]
if ( bufferedAnnotations )
{
@ -579,7 +605,6 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
}
} // end current annotation drawing
}
if(viewPortPoint)
{
QPainter painter(&backImage);
@ -609,6 +634,7 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
// 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 );
@ -641,6 +667,7 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
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();
@ -684,23 +711,24 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
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);
}
}
@ -713,12 +741,16 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
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 );
@ -823,8 +855,10 @@ void PagePainter::paintCroppedPageOnPainter( QPainter * destPainter, const Okula
/** 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);
@ -832,7 +866,8 @@ void PagePainter::cropPixmapOnImage( QImage & dest, const QPixmap * src, const Q
// 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();
@ -866,40 +901,6 @@ void PagePainter::recolor(QImage *image, const QColor &foreground, const QColor
}
}
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<unsigned int> 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; }

View file

@ -56,12 +56,6 @@ class Q_DECL_EXPORT PagePainter
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 );

View file

@ -1651,8 +1651,10 @@ void PageView::paintEvent(QPaintEvent *pe)
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
@ -1666,11 +1668,12 @@ void PageView::paintEvent(QPaintEvent *pe)
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 );
@ -1697,11 +1700,12 @@ void PageView::paintEvent(QPaintEvent *pe)
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 );

View file

@ -36,6 +36,7 @@ class DocumentViewport;
class Annotation;
class MovieAction;
class RenditionAction;
class PixmapRequest;
}
class FormWidgetIface;

View file

@ -1111,11 +1111,18 @@ QString PageViewAnnotator::defaultToolName( const QDomElement &toolElement )
QPixmap PageViewAnnotator::makeToolPixmap( const QDomElement &toolElement )
{
QPixmap pixmap( 32, 32 );
QPixmap pixmap( 32 * qApp->devicePixelRatio(), 32 * qApp->devicePixelRatio() );
pixmap.setDevicePixelRatio( qApp->devicePixelRatio() );
const QString annotType = toolElement.attribute( QStringLiteral("type") );
// Load HiDPI variant on HiDPI screen
QString imageVariant;
if ( qApp->devicePixelRatio() > 1.05 ) {
imageVariant = "@2x";
}
// Load base pixmap. We'll draw on top of it
pixmap.load( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/tool-base-okular.png") ) );
pixmap.load( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-base-okular" + imageVariant + ".png") ) );
/* Parse color, innerColor and icon (if present) */
QColor engineColor, innerColor;
@ -1149,7 +1156,7 @@ QPixmap PageViewAnnotator::makeToolPixmap( const QDomElement &toolElement )
}
else if ( annotType == QLatin1String("highlight") )
{
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/tool-highlighter-okular-colorizable.png") ) );
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-highlighter-okular-colorizable" + imageVariant + ".png") ) );
QImage colorizedOverlay = overlay;
GuiUtils::colorizeImage( colorizedOverlay, engineColor );
@ -1159,7 +1166,7 @@ QPixmap PageViewAnnotator::makeToolPixmap( const QDomElement &toolElement )
}
else if ( annotType == QLatin1String("ink") )
{
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/tool-ink-okular-colorizable.png") ) );
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-ink-okular-colorizable" + imageVariant + ".png") ) );
QImage colorizedOverlay = overlay;
GuiUtils::colorizeImage( colorizedOverlay, engineColor );
@ -1169,13 +1176,13 @@ QPixmap PageViewAnnotator::makeToolPixmap( const QDomElement &toolElement )
}
else if ( annotType == QLatin1String("note-inline") )
{
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/tool-note-inline-okular-colorizable.png") ) );
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-note-inline-okular-colorizable" + imageVariant + ".png") ) );
GuiUtils::colorizeImage( overlay, engineColor );
p.drawImage( QPoint(0,0), overlay );
}
else if ( annotType == QLatin1String("note-linked") )
{
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/tool-note-okular-colorizable.png") ) );
QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-note-okular-colorizable.png" + imageVariant + ".png") ) );
GuiUtils::colorizeImage( overlay, engineColor );
p.drawImage( QPoint(0,0), overlay );
}

View file

@ -96,6 +96,7 @@ struct PresentationFrame
geometry.setRect( ( width - pageWidth ) / 2,
( height - pageHeight ) / 2,
pageWidth, pageHeight );
Q_FOREACH ( VideoWidget *vw, videoWidgets )
{
const Okular::NormalizedRect r = vw->normGeometry();
@ -451,7 +452,7 @@ void PresentationWidget::notifyCurrentPageChanged( int previousPage, int current
// if pixmap not inside the Okular::Page we request it and wait for
// notifyPixmapChanged call or else we can proceed to pixmap generation
if ( !frame->page->hasPixmap( this, pixW, pixH ) )
if ( !frame->page->hasPixmap( this, ceil(pixW * qApp->devicePixelRatio()), ceil(pixH * qApp->devicePixelRatio()) ) )
{
requestPixmaps();
}
@ -780,6 +781,8 @@ void PresentationWidget::mouseMoveEvent( QMouseEvent * e )
void PresentationWidget::paintEvent( QPaintEvent * pe )
{
qreal dpr = devicePixelRatioF();
if ( m_inBlackScreenMode )
{
QPainter painter( this );
@ -824,28 +827,32 @@ void PresentationWidget::paintEvent( QPaintEvent * pe )
if ( !r.isValid() )
continue;
#ifdef ENABLE_PROGRESS_OVERLAY
const QRect dR(QRectF(r.x() * dpr, r.y() * dpr, r.width() * dpr, r.height() * dpr).toAlignedRect());
if ( Okular::Settings::slidesShowProgress() && r.intersects( m_overlayGeometry ) )
{
// backbuffer the overlay operation
QPixmap backPixmap( r.size() );
QPixmap backPixmap( dR.size() );
backPixmap.setDevicePixelRatio( dpr );
QPainter pixPainter( &backPixmap );
// first draw the background on the backbuffer
pixPainter.drawPixmap( QPoint(0,0), m_lastRenderedPixmap, r );
pixPainter.drawPixmap( QPoint(0,0), m_lastRenderedPixmap, dR );
// then blend the overlay (a piece of) over the background
QRect ovr = m_overlayGeometry.intersected( r );
pixPainter.drawPixmap( ovr.left() - r.left(), ovr.top() - r.top(),
m_lastRenderedOverlay, ovr.left() - m_overlayGeometry.left(),
ovr.top() - m_overlayGeometry.top(), ovr.width(), ovr.height() );
pixPainter.drawPixmap( (ovr.left() - r.left()), (ovr.top() - r.top()),
m_lastRenderedOverlay, (ovr.left() - m_overlayGeometry.left()) * dpr,
(ovr.top() - m_overlayGeometry.top()) * dpr, ovr.width() * dpr, ovr.height() * dpr );
// finally blit the pixmap to the screen
pixPainter.end();
painter.drawPixmap( r.topLeft(), backPixmap, backPixmap.rect() );
const QRect backPixmapRect = backPixmap.rect();
const QRect dBackPixmapRect(QRectF(backPixmapRect.x() * dpr, backPixmapRect.y() * dpr, backPixmapRect.width() * dpr, backPixmapRect.height() * dpr).toAlignedRect());
painter.drawPixmap( r.topLeft(), backPixmap, dBackPixmapRect );
} else
#endif
// copy the rendered pixmap to the screen
painter.drawPixmap( r.topLeft(), m_lastRenderedPixmap, r );
painter.drawPixmap( r.topLeft(), m_lastRenderedPixmap, dR );
}
// paint drawings
@ -1001,7 +1008,10 @@ void PresentationWidget::generatePage( bool disableTransition )
{
if ( m_lastRenderedPixmap.isNull() )
{
m_lastRenderedPixmap = QPixmap( m_width, m_height );
qreal dpr = qApp->devicePixelRatio();
m_lastRenderedPixmap = QPixmap( m_width * dpr, m_height * dpr );
m_lastRenderedPixmap.setDevicePixelRatio(dpr);
m_previousPagePixmap = QPixmap();
}
else
@ -1054,6 +1064,8 @@ void PresentationWidget::generatePage( bool disableTransition )
void PresentationWidget::generateIntroPage( QPainter & p )
{
qreal dpr = qApp->devicePixelRatio();
// use a vertical gray gradient background
int blend1 = m_height / 10,
blend2 = 9 * m_height / 10;
@ -1069,7 +1081,8 @@ void PresentationWidget::generateIntroPage( QPainter & p )
}
// draw okular logo in the four corners
QPixmap logo = DesktopIcon( QStringLiteral("okular"), 64 );
QPixmap logo = DesktopIcon( QStringLiteral("okular"), 64 * dpr );
logo.setDevicePixelRatio( dpr );
if ( !logo.isNull() )
{
p.drawPixmap( 5, 5, logo );
@ -1116,6 +1129,7 @@ void PresentationWidget::generateContentsPage( int pageNum, QPainter & p )
// draw the page using the shared PagePainter class
int flags = PagePainter::Accessibility | PagePainter::Highlights | PagePainter::Annotations;
PagePainter::paintPageOnPainter( &p, frame->page, this, flags,
geom.width(), geom.height(), geom );
@ -1137,6 +1151,8 @@ inline int qt_div255(int x) { return (x + (x>>8) + 0x80) >> 8; }
void PresentationWidget::generateOverlay()
{
#ifdef ENABLE_PROGRESS_OVERLAY
qreal dpr = qApp->devicePixelRatio();
// calculate overlay geometry and resize pixmap if needed
int side = m_width / 16;
m_overlayGeometry.setRect( m_width - side - 4, 4, side, side );
@ -1145,7 +1161,9 @@ void PresentationWidget::generateOverlay()
// and the resulting image is smoothly scaled down. So here we open a
// painter on the double sized pixmap.
side *= 2;
QPixmap doublePixmap( side, side );
QPixmap doublePixmap( side * dpr, side * dpr );
doublePixmap.setDevicePixelRatio( dpr );
doublePixmap.fill( Qt::black );
QPainter pixmapPainter( &doublePixmap );
pixmapPainter.setRenderHints( QPainter::Antialiasing );
@ -1190,8 +1208,10 @@ void PresentationWidget::generateOverlay()
// end drawing pixmap and halve image
pixmapPainter.end();
QImage image( doublePixmap.toImage().scaled( side / 2, side / 2, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
QImage image( doublePixmap.toImage().scaled( (side / 2) * dpr, (side / 2) * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
image.setDevicePixelRatio( dpr );
image = image.convertToFormat( QImage::Format_ARGB32 );
image.setDevicePixelRatio( dpr );
// draw circular shadow using the same technique
doublePixmap.fill( Qt::black );
@ -1200,7 +1220,8 @@ void PresentationWidget::generateOverlay()
pixmapPainter.setBrush( QColor( 0x80 ) );
pixmapPainter.drawEllipse( 0, 0, side, side );
pixmapPainter.end();
QImage shadow( doublePixmap.toImage().scaled( side / 2, side / 2, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
QImage shadow( doublePixmap.toImage().scaled( (side / 2) * dpr, (side / 2) * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ) );
shadow.setDevicePixelRatio( dpr );
// generate a 2 colors pixmap using mixing shadow (made with highlight color)
// and image (made with highlightedText color)
@ -1238,6 +1259,7 @@ void PresentationWidget::generateOverlay()
data[i] = qRgba( cR, cG, cB, cA );
}
m_lastRenderedOverlay = QPixmap::fromImage( image );
m_lastRenderedOverlay.setDevicePixelRatio( dpr );
// start the autohide timer
//repaint( m_overlayGeometry ); // toggle with next line
@ -1519,6 +1541,7 @@ void PresentationWidget::slotTransitionStep()
QPainter pixmapPainter;
m_currentPixmapOpacity += 1.0 / m_transitionSteps;
m_lastRenderedPixmap = QPixmap( m_lastRenderedPixmap.size() );
m_lastRenderedPixmap.setDevicePixelRatio( qApp->devicePixelRatio() );
m_lastRenderedPixmap.fill( Qt::transparent );
pixmapPainter.begin( &m_lastRenderedPixmap );
pixmapPainter.setCompositionMode( QPainter::CompositionMode_Source );