Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Backports:SLE-15-SP1:Update
gwenview5
0003-HiDPI-Support-for-Gwenview.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0003-HiDPI-Support-for-Gwenview.patch of Package gwenview5
From 1370552e49406d2869f56eea57a3ab8e1a17e1b3 Mon Sep 17 00:00:00 2001 From: Alexander Volkov <a.volkov@rusbitech.ru> Date: Mon, 1 Apr 2019 16:40:35 +0300 Subject: [PATCH] HiDPI Support for Gwenview Summary: Initial support for HiDPI-scaling of documents in RasterImageView. This patch scales up images to display them correctly on HiDPI-enabled screens. TODO: - SVG documents and videos - Scaling of thumbnails BUG: 373178 Reviewers: davidedmundson, rkflx, hetzenecker, ngraham, #gwenview Reviewed By: ngraham, #gwenview Subscribers: volkov, asturmlechner, fvogt, abalaji, rkflx, ngraham, anthonyfieroni, cfeck, asn Tags: #gwenview Differential Revision: https://phabricator.kde.org/D7581 --- lib/documentview/abstractimageview.cpp | 36 +++++++++++++------- lib/documentview/abstractimageview.h | 12 +++++++ lib/documentview/birdeyeview.cpp | 4 +-- lib/documentview/documentview.cpp | 7 +++- lib/documentview/rasterimageview.cpp | 15 ++++++--- lib/imagescaler.cpp | 60 +++++++++++++++++++++------------- 6 files changed, 93 insertions(+), 41 deletions(-) diff --git a/lib/documentview/abstractimageview.cpp b/lib/documentview/abstractimageview.cpp index bcd77c21..25b9a35f 100644 --- a/lib/documentview/abstractimageview.cpp +++ b/lib/documentview/abstractimageview.cpp @@ -32,6 +32,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA #include <QGraphicsSceneMouseEvent> #include <QStandardPaths> #include <QPainter> +#include <QApplication> namespace Gwenview { @@ -63,7 +64,7 @@ struct AbstractImageViewPrivate void adjustImageOffset(Verbosity verbosity = Notify) { - QSizeF zoomedDocSize = q->documentSize() * mZoom; + QSizeF zoomedDocSize = q->dipDocumentSize() * mZoom; QSizeF viewSize = q->boundingRect().size(); QPointF offset( qMax((viewSize.width() - zoomedDocSize.width()) / 2, qreal(0.)), @@ -88,7 +89,7 @@ struct AbstractImageViewPrivate mScrollPos = _newPos; return; } - QSizeF zoomedDocSize = q->documentSize() * mZoom; + QSizeF zoomedDocSize = q->dipDocumentSize() * mZoom; QSizeF viewSize = q->boundingRect().size(); QPointF newPos( qBound(qreal(0.), _newPos.x(), zoomedDocSize.width() - viewSize.width()), @@ -123,6 +124,7 @@ struct AbstractImageViewPrivate const QColor light = QColor(192, 192, 192); painter.fillRect(0, 0, 16, 16, light); painter.fillRect(16, 16, 16, 16, light); + pix.setDevicePixelRatio(q->devicePixelRatio()); return pix; } @@ -185,6 +187,11 @@ QSizeF AbstractImageView::documentSize() const return d->mDocument ? d->mDocument->size() : QSizeF(); } +QSizeF AbstractImageView::dipDocumentSize() const +{ + return d->mDocument ? d->mDocument->size() / devicePixelRatio(): QSizeF(); +} + qreal AbstractImageView::zoom() const { return d->mZoom; @@ -335,7 +342,7 @@ void AbstractImageView::focusInEvent(QFocusEvent* event) qreal AbstractImageView::computeZoomToFit() const { - QSizeF docSize = documentSize(); + QSizeF docSize = dipDocumentSize(); if (docSize.isEmpty()) { return 1; } @@ -351,7 +358,7 @@ qreal AbstractImageView::computeZoomToFit() const qreal AbstractImageView::computeZoomToFill() const { - QSizeF docSize = documentSize(); + QSizeF docSize = dipDocumentSize(); if (docSize.isEmpty()) { return 1; } @@ -483,7 +490,7 @@ void AbstractImageView::keyPressEvent(QKeyEvent* event) d->setScrollPos(QPointF(d->mScrollPos.x(), 0)); return; case Qt::Key_End: - d->setScrollPos(QPointF(d->mScrollPos.x(), documentSize().height() * zoom())); + d->setScrollPos(QPointF(d->mScrollPos.x(), dipDocumentSize().height() * zoom())); return; default: return; @@ -523,9 +530,14 @@ void AbstractImageView::setScrollPos(const QPointF& pos) d->setScrollPos(pos); } +qreal AbstractImageView::devicePixelRatio() const +{ + return qApp->devicePixelRatio(); +} + QPointF AbstractImageView::mapToView(const QPointF& imagePos) const { - return imagePos * d->mZoom + d->mImageOffset - d->mScrollPos; + return imagePos / devicePixelRatio() * d->mZoom + d->mImageOffset - d->mScrollPos; } QPoint AbstractImageView::mapToView(const QPoint& imagePos) const @@ -537,7 +549,7 @@ QRectF AbstractImageView::mapToView(const QRectF& imageRect) const { return QRectF( mapToView(imageRect.topLeft()), - imageRect.size() * zoom() + imageRect.size() * zoom() / devicePixelRatio() ); } @@ -545,13 +557,13 @@ QRect AbstractImageView::mapToView(const QRect& imageRect) const { return QRect( mapToView(imageRect.topLeft()), - imageRect.size() * zoom() + imageRect.size() * zoom() / devicePixelRatio() ); } QPointF AbstractImageView::mapToImage(const QPointF& viewPos) const { - return (viewPos - d->mImageOffset + d->mScrollPos) / d->mZoom; + return (viewPos - d->mImageOffset + d->mScrollPos) / d->mZoom * devicePixelRatio(); } QPoint AbstractImageView::mapToImage(const QPoint& viewPos) const @@ -563,7 +575,7 @@ QRectF AbstractImageView::mapToImage(const QRectF& viewRect) const { return QRectF( mapToImage(viewRect.topLeft()), - viewRect.size() / zoom() + viewRect.size() / zoom() * devicePixelRatio() ); } @@ -571,7 +583,7 @@ QRect AbstractImageView::mapToImage(const QRect& viewRect) const { return QRect( mapToImage(viewRect.topLeft()), - viewRect.size() / zoom() + viewRect.size() / zoom() * devicePixelRatio() ); } @@ -601,7 +613,7 @@ QSizeF AbstractImageView::visibleImageSize() const if (!document()) { return QSizeF(); } - QSizeF size = documentSize() * zoom(); + QSizeF size = dipDocumentSize() * zoom(); return size.boundedTo(boundingRect().size()); } diff --git a/lib/documentview/abstractimageview.h b/lib/documentview/abstractimageview.h index f5a211e9..807c2aa2 100644 --- a/lib/documentview/abstractimageview.h +++ b/lib/documentview/abstractimageview.h @@ -75,12 +75,22 @@ public: QSizeF documentSize() const; + /** + * Returns the size of the loaded document in device independent pixels. + */ + QSizeF dipDocumentSize() const; + + /* + * The size of the image that is currently visible, + * in device independent pixels. + */ QSizeF visibleImageSize() const; /** * If the image is smaller than the view, imageOffset is the distance from * the topleft corner of the view to the topleft corner of the image. * Neither x nor y can be negative. + * This is in device independent pixels. */ QPointF imageOffset() const; @@ -91,6 +101,8 @@ public: QPointF scrollPos() const; void setScrollPos(const QPointF& pos); + qreal devicePixelRatio() const; + QPointF mapToView(const QPointF& imagePos) const; QPoint mapToView(const QPoint& imagePos) const; QRectF mapToView(const QRectF& imageRect) const; diff --git a/lib/documentview/birdeyeview.cpp b/lib/documentview/birdeyeview.cpp index 55dd5ba7..9f35a4b1 100644 --- a/lib/documentview/birdeyeview.cpp +++ b/lib/documentview/birdeyeview.cpp @@ -138,7 +138,7 @@ void BirdEyeView::adjustGeometry() if (!d->mDocView->canZoom() || d->mDocView->zoomToFit()) { return; } - QSizeF size = d->mDocView->document()->size(); + QSizeF size = d->mDocView->document()->size() / qApp->devicePixelRatio(); size.scale(MIN_SIZE, MIN_SIZE, Qt::KeepAspectRatioByExpanding); QRectF docViewRect = d->mDocView->boundingRect(); int maxBevHeight = docViewRect.height() - 2 * VIEW_OFFSET; @@ -163,7 +163,7 @@ void BirdEyeView::adjustGeometry() void BirdEyeView::adjustVisibleRect() { - QSizeF docSize = d->mDocView->document()->size(); + QSizeF docSize = d->mDocView->document()->size() / qApp->devicePixelRatio(); qreal viewZoom = d->mDocView->zoom(); qreal bevZoom; if (docSize.height() > docSize.width()) { diff --git a/lib/documentview/documentview.cpp b/lib/documentview/documentview.cpp index 2035df28..f7a4359f 100644 --- a/lib/documentview/documentview.cpp +++ b/lib/documentview/documentview.cpp @@ -42,6 +42,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA #include <QDrag> #include <QMimeData> #include <QStyleHints> +#include <QGestureEvent> +#include <QLibraryInfo> // KDE #include <KLocalizedString> @@ -455,7 +457,10 @@ DocumentView::DocumentView(QGraphicsScene* scene) // This is important for fade effects, where we don't want any background layers visible during the fade. d->mOpacityEffect = new QGraphicsOpacityEffect(this); d->mOpacityEffect->setOpacity(0); - setGraphicsEffect(d->mOpacityEffect); + + // QTBUG-74963. QGraphicsOpacityEffect cause painting an image as non-highdpi. + if (qFuzzyCompare(qApp->devicePixelRatio(), 1.0) || QLibraryInfo::version() >= QVersionNumber(5, 12, 4)) + setGraphicsEffect(d->mOpacityEffect); scene->addItem(this); diff --git a/lib/documentview/rasterimageview.cpp b/lib/documentview/rasterimageview.cpp index a0aa86ad..da2a28a1 100644 --- a/lib/documentview/rasterimageview.cpp +++ b/lib/documentview/rasterimageview.cpp @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA #include <lib/imagescaler.h> #include <lib/cms/cmsprofile.h> #include <lib/gvdebug.h> +#include <lib/paintutils.h> // KDE @@ -35,6 +36,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA #include <QTimer> #include <QPointer> #include <QDebug> +#include <QApplication> namespace Gwenview @@ -139,8 +141,9 @@ struct RasterImageViewPrivate void resizeBuffer() { + const auto dpr = q->devicePixelRatio(); QSize size = q->visibleImageSize().toSize(); - if (size == mCurrentBuffer.size()) { + if (size * dpr == mCurrentBuffer.size()) { return; } if (!size.isValid()) { @@ -149,7 +152,8 @@ struct RasterImageViewPrivate return; } - mAlternateBuffer = QPixmap(size); + mAlternateBuffer = QPixmap(size * dpr); + mAlternateBuffer.setDevicePixelRatio(dpr); mAlternateBuffer.fill(Qt::transparent); { QPainter painter(&mAlternateBuffer); @@ -384,6 +388,7 @@ void RasterImageView::onScrollPosChanged(const QPointF& oldPos) { if (d->mAlternateBuffer.size() != d->mCurrentBuffer.size()) { d->mAlternateBuffer = QPixmap(d->mCurrentBuffer.size()); + d->mAlternateBuffer.setDevicePixelRatio(d->mCurrentBuffer.devicePixelRatio()); } d->mAlternateBuffer.fill(Qt::transparent); QPainter painter(&d->mAlternateBuffer); @@ -392,7 +397,7 @@ void RasterImageView::onScrollPosChanged(const QPointF& oldPos) qSwap(d->mCurrentBuffer, d->mAlternateBuffer); // Scale missing parts - QRegion bufferRegion = QRegion(d->mCurrentBuffer.rect().translated(scrollPos().toPoint())); + QRegion bufferRegion = QRect(scrollPos().toPoint(), d->mCurrentBuffer.size() / devicePixelRatio()); QRegion updateRegion = bufferRegion - bufferRegion.translated(-delta.toPoint()); updateBuffer(updateRegion); update(); @@ -400,13 +405,15 @@ void RasterImageView::onScrollPosChanged(const QPointF& oldPos) void RasterImageView::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) { + d->mCurrentBuffer.setDevicePixelRatio(devicePixelRatio()); + QPointF topLeft = imageOffset(); if (zoomToFit()) { // In zoomToFit mode, scale crudely the buffer to fit the screen. This // provide an approximate rendered which will be replaced when the scheduled // proper scale is ready. // Round point and size independently, to keep consistency with the below (non zoomToFit) painting - const QRect rect = QRect(topLeft.toPoint(), (documentSize() * zoom()).toSize()); + const QRect rect = QRect(topLeft.toPoint(), (dipDocumentSize() * zoom()).toSize()); painter->drawPixmap(rect, d->mCurrentBuffer); } else { painter->drawPixmap(topLeft.toPoint(), d->mCurrentBuffer); diff --git a/lib/imagescaler.cpp b/lib/imagescaler.cpp index 75a38798..b6acb4c2 100644 --- a/lib/imagescaler.cpp +++ b/lib/imagescaler.cpp @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include <QImage> #include <QRegion> #include <QDebug> +#include <QApplication> // KDE @@ -45,6 +46,19 @@ namespace Gwenview // Amount of pixels to keep so that smooth scale is correct static const int SMOOTH_MARGIN = 3; +static inline QRectF scaledRect(const QRectF& rect, qreal factor) +{ + return QRectF(rect.x() * factor, + rect.y() * factor, + rect.width() * factor, + rect.height() * factor); +} + +static inline QRect scaledRect(const QRect& rect, qreal factor) +{ + return scaledRect(QRectF(rect), factor).toAlignedRect(); +} + struct ImageScalerPrivate { Qt::TransformationMode mTransformationMode; @@ -126,9 +140,15 @@ void ImageScaler::doScale() void ImageScaler::scaleRect(const QRect& rect) { + const qreal dpr = qApp->devicePixelRatio(); + + // variables prefixed with dp are in device pixels + const QRect dpRect = Gwenview::scaledRect(rect, dpr); + const qreal REAL_DELTA = 0.001; if (qAbs(d->mZoom - 1.0) < REAL_DELTA) { - QImage tmp = d->mDocument->image().copy(rect); + QImage tmp = d->mDocument->image().copy(dpRect); + tmp.setDevicePixelRatio(dpr); emit scaledRect(rect.left(), rect.top(), tmp); return; } @@ -144,14 +164,12 @@ void ImageScaler::scaleRect(const QRect& rect) image = d->mDocument->image(); zoom = d->mZoom; } + const QRect imageRect = Gwenview::scaledRect(image.rect(), 1.0 / dpr); + // If rect contains "half" pixels, make sure sourceRect includes them - QRectF sourceRectF( - rect.left() / zoom, - rect.top() / zoom, - rect.width() / zoom, - rect.height() / zoom); + QRectF sourceRectF = Gwenview::scaledRect(QRectF(rect), 1.0 / zoom); - sourceRectF = sourceRectF.intersected(image.rect()); + sourceRectF = sourceRectF.intersected(imageRect); QRect sourceRect = PaintUtils::containingRect(sourceRectF); if (sourceRect.isEmpty()) { return; @@ -165,8 +183,8 @@ void ImageScaler::scaleRect(const QRect& rect) if (needsSmoothMargins) { sourceLeftMargin = qMin(sourceRect.left(), SMOOTH_MARGIN); sourceTopMargin = qMin(sourceRect.top(), SMOOTH_MARGIN); - sourceRightMargin = qMin(image.rect().right() - sourceRect.right(), SMOOTH_MARGIN); - sourceBottomMargin = qMin(image.rect().bottom() - sourceRect.bottom(), SMOOTH_MARGIN); + sourceRightMargin = qMin(imageRect.right() - sourceRect.right(), SMOOTH_MARGIN); + sourceBottomMargin = qMin(imageRect.bottom() - sourceRect.bottom(), SMOOTH_MARGIN); sourceRect.adjust( -sourceLeftMargin, -sourceTopMargin, @@ -182,30 +200,28 @@ void ImageScaler::scaleRect(const QRect& rect) } // destRect is almost like rect, but it contains only "full" pixels - QRectF destRectF = QRectF( - sourceRect.left() * zoom, - sourceRect.top() * zoom, - sourceRect.width() * zoom, - sourceRect.height() * zoom - ); - QRect destRect = PaintUtils::containingRect(destRectF); + QRect destRect = Gwenview::scaledRect(sourceRect, zoom); + + QRect dpSourceRect = Gwenview::scaledRect(sourceRect, dpr); + QRect dpDestRect = Gwenview::scaledRect(dpSourceRect, zoom); QImage tmp; - tmp = image.copy(sourceRect); + tmp = image.copy(dpSourceRect); tmp = tmp.scaled( - destRect.width(), - destRect.height(), + dpDestRect.width(), + dpDestRect.height(), Qt::IgnoreAspectRatio, // Do not use KeepAspectRatio, it can lead to skipped rows or columns d->mTransformationMode); if (needsSmoothMargins) { tmp = tmp.copy( - destLeftMargin, destTopMargin, - destRect.width() - (destLeftMargin + destRightMargin), - destRect.height() - (destTopMargin + destBottomMargin) + destLeftMargin * dpr, destTopMargin * dpr, + dpDestRect.width() - (destLeftMargin + destRightMargin) * dpr, + dpDestRect.height() - (destTopMargin + destBottomMargin) * dpr ); } + tmp.setDevicePixelRatio(dpr); emit scaledRect(destRect.left() + destLeftMargin, destRect.top() + destTopMargin, tmp); } -- 2.16.4
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor