Commit ecc1141e authored by Lukas Hetzenecker's avatar Lukas Hetzenecker

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
parent 6d7403ff
......@@ -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();
......
......@@ -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;
......
......@@ -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;
......
......@@ -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();
......
......@@ -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
......
This diff is collapsed.
......@@ -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 );
......
......@@ -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 );
......
......@@ -36,6 +36,7 @@ class DocumentViewport;
class Annotation;
class MovieAction;
class RenditionAction;
class PixmapRequest;
}
class FormWidgetIface;
......
......@@ -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 );
}
......
......@@ -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 );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment